blob: af870d12d5975c9d7330daf92e5b1cb39541c048 [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/p2p/base/stun.h"
12
13#include <string.h>
14
kwiberg3ec46792016-04-27 07:22:53 -070015#include <memory>
16
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000017#include "webrtc/base/byteorder.h"
nisseede5da42017-01-12 05:15:36 -080018#include "webrtc/base/checks.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000019#include "webrtc/base/crc32.h"
20#include "webrtc/base/logging.h"
21#include "webrtc/base/messagedigest.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000022#include "webrtc/base/stringencode.h"
23
jbauchf1f87202016-03-30 06:43:37 -070024using rtc::ByteBufferReader;
25using rtc::ByteBufferWriter;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000026
27namespace cricket {
28
29const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[] = "Try Alternate Server";
30const char STUN_ERROR_REASON_BAD_REQUEST[] = "Bad Request";
31const char STUN_ERROR_REASON_UNAUTHORIZED[] = "Unauthorized";
32const char STUN_ERROR_REASON_FORBIDDEN[] = "Forbidden";
33const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "Stale Credentials";
34const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[] = "Allocation Mismatch";
35const char STUN_ERROR_REASON_STALE_NONCE[] = "Stale Nonce";
36const char STUN_ERROR_REASON_WRONG_CREDENTIALS[] = "Wrong Credentials";
37const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[] = "Unsupported Protocol";
38const char STUN_ERROR_REASON_ROLE_CONFLICT[] = "Role Conflict";
39const char STUN_ERROR_REASON_SERVER_ERROR[] = "Server Error";
40
41const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
42const char EMPTY_TRANSACTION_ID[] = "0000000000000000";
Peter Boström0c4e06b2015-10-07 12:23:21 +020043const uint32_t STUN_FINGERPRINT_XOR_VALUE = 0x5354554E;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000044
45// StunMessage
46
47StunMessage::StunMessage()
48 : type_(0),
49 length_(0),
50 transaction_id_(EMPTY_TRANSACTION_ID) {
nisseede5da42017-01-12 05:15:36 -080051 RTC_DCHECK(IsValidTransactionId(transaction_id_));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000052}
53
54bool StunMessage::IsLegacy() const {
55 if (transaction_id_.size() == kStunLegacyTransactionIdLength)
56 return true;
nisseede5da42017-01-12 05:15:36 -080057 RTC_DCHECK(transaction_id_.size() == kStunTransactionIdLength);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000058 return false;
59}
60
61bool StunMessage::SetTransactionID(const std::string& str) {
62 if (!IsValidTransactionId(str)) {
63 return false;
64 }
65 transaction_id_ = str;
66 return true;
67}
68
nissecc99bc22017-02-02 01:31:30 -080069void StunMessage::AddAttribute(StunAttribute* attr) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000070 // Fail any attributes that aren't valid for this type of message.
nissecc99bc22017-02-02 01:31:30 -080071 RTC_DCHECK_EQ(attr->value_type(), GetAttributeValueType(attr->type()));
72
zsteinad94c4c2017-03-06 13:36:05 -080073 attrs_.emplace_back(attr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000074 attr->SetOwner(this);
75 size_t attr_length = attr->length();
76 if (attr_length % 4 != 0) {
77 attr_length += (4 - (attr_length % 4));
78 }
Peter Boström0c4e06b2015-10-07 12:23:21 +020079 length_ += static_cast<uint16_t>(attr_length + 4);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000080}
81
82const StunAddressAttribute* StunMessage::GetAddress(int type) const {
83 switch (type) {
84 case STUN_ATTR_MAPPED_ADDRESS: {
85 // Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
86 // missing.
87 const StunAttribute* mapped_address =
88 GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
89 if (!mapped_address)
90 mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
91 return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
92 }
93
94 default:
95 return static_cast<const StunAddressAttribute*>(GetAttribute(type));
96 }
97}
98
99const StunUInt32Attribute* StunMessage::GetUInt32(int type) const {
100 return static_cast<const StunUInt32Attribute*>(GetAttribute(type));
101}
102
103const StunUInt64Attribute* StunMessage::GetUInt64(int type) const {
104 return static_cast<const StunUInt64Attribute*>(GetAttribute(type));
105}
106
107const StunByteStringAttribute* StunMessage::GetByteString(int type) const {
108 return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
109}
110
111const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
112 return static_cast<const StunErrorCodeAttribute*>(
113 GetAttribute(STUN_ATTR_ERROR_CODE));
114}
115
116const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
117 return static_cast<const StunUInt16ListAttribute*>(
118 GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
119}
120
121// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
122// procedure outlined in RFC 5389, section 15.4.
123bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size,
124 const std::string& password) {
125 // Verifying the size of the message.
katrielce4bda242016-06-09 08:45:45 -0700126 if ((size % 4) != 0 || size < kStunHeaderSize) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000127 return false;
128 }
129
130 // Getting the message length from the STUN header.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200131 uint16_t msg_length = rtc::GetBE16(&data[2]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000132 if (size != (msg_length + kStunHeaderSize)) {
133 return false;
134 }
135
136 // Finding Message Integrity attribute in stun message.
137 size_t current_pos = kStunHeaderSize;
138 bool has_message_integrity_attr = false;
katrielc1a206102016-06-20 05:13:16 -0700139 while (current_pos + 4 <= size) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200140 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000141 // Getting attribute type and length.
142 attr_type = rtc::GetBE16(&data[current_pos]);
143 attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
144
145 // If M-I, sanity check it, and break out.
146 if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
147 if (attr_length != kStunMessageIntegritySize ||
katrielc1a206102016-06-20 05:13:16 -0700148 current_pos + sizeof(attr_type) + sizeof(attr_length) + attr_length >
149 size) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000150 return false;
151 }
152 has_message_integrity_attr = true;
153 break;
154 }
155
156 // Otherwise, skip to the next attribute.
157 current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
158 if ((attr_length % 4) != 0) {
159 current_pos += (4 - (attr_length % 4));
160 }
161 }
162
163 if (!has_message_integrity_attr) {
164 return false;
165 }
166
167 // Getting length of the message to calculate Message Integrity.
168 size_t mi_pos = current_pos;
kwiberg3ec46792016-04-27 07:22:53 -0700169 std::unique_ptr<char[]> temp_data(new char[current_pos]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000170 memcpy(temp_data.get(), data, current_pos);
171 if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
172 // Stun message has other attributes after message integrity.
173 // Adjust the length parameter in stun message to calculate HMAC.
174 size_t extra_offset = size -
175 (mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
176 size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
177
178 // Writing new length of the STUN message @ Message Length in temp buffer.
179 // 0 1 2 3
180 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
181 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
182 // |0 0| STUN Message Type | Message Length |
183 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Peter Boström0c4e06b2015-10-07 12:23:21 +0200184 rtc::SetBE16(temp_data.get() + 2, static_cast<uint16_t>(new_adjusted_len));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000185 }
186
187 char hmac[kStunMessageIntegritySize];
188 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
189 password.c_str(), password.size(),
190 temp_data.get(), mi_pos,
191 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800192 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000193 if (ret != sizeof(hmac))
194 return false;
195
196 // Comparing the calculated HMAC with the one present in the message.
197 return memcmp(data + current_pos + kStunAttributeHeaderSize,
198 hmac,
199 sizeof(hmac)) == 0;
200}
201
202bool StunMessage::AddMessageIntegrity(const std::string& password) {
203 return AddMessageIntegrity(password.c_str(), password.size());
204}
205
206bool StunMessage::AddMessageIntegrity(const char* key,
207 size_t keylen) {
208 // Add the attribute with a dummy value. Since this is a known attribute, it
209 // can't fail.
210 StunByteStringAttribute* msg_integrity_attr =
211 new StunByteStringAttribute(STUN_ATTR_MESSAGE_INTEGRITY,
212 std::string(kStunMessageIntegritySize, '0'));
nissecc99bc22017-02-02 01:31:30 -0800213 AddAttribute(msg_integrity_attr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000214
215 // Calculate the HMAC for the message.
jbauchf1f87202016-03-30 06:43:37 -0700216 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000217 if (!Write(&buf))
218 return false;
219
220 int msg_len_for_hmac = static_cast<int>(
221 buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
222 char hmac[kStunMessageIntegritySize];
223 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
224 key, keylen,
225 buf.Data(), msg_len_for_hmac,
226 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800227 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000228 if (ret != sizeof(hmac)) {
229 LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
230 << "has dummy value.";
231 return false;
232 }
233
234 // Insert correct HMAC into the attribute.
235 msg_integrity_attr->CopyBytes(hmac, sizeof(hmac));
236 return true;
237}
238
239// Verifies a message is in fact a STUN message, by performing the checks
240// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
241// in section 15.5.
242bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
243 // Check the message length.
244 size_t fingerprint_attr_size =
245 kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
246 if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
247 return false;
248
249 // Skip the rest if the magic cookie isn't present.
250 const char* magic_cookie =
251 data + kStunTransactionIdOffset - kStunMagicCookieLength;
252 if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
253 return false;
254
255 // Check the fingerprint type and length.
256 const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
257 if (rtc::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
Peter Boström0c4e06b2015-10-07 12:23:21 +0200258 rtc::GetBE16(fingerprint_attr_data + sizeof(uint16_t)) !=
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000259 StunUInt32Attribute::SIZE)
260 return false;
261
262 // Check the fingerprint value.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200263 uint32_t fingerprint =
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000264 rtc::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
265 return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
266 rtc::ComputeCrc32(data, size - fingerprint_attr_size));
267}
268
269bool StunMessage::AddFingerprint() {
270 // Add the attribute with a dummy value. Since this is a known attribute,
271 // it can't fail.
272 StunUInt32Attribute* fingerprint_attr =
273 new StunUInt32Attribute(STUN_ATTR_FINGERPRINT, 0);
nissecc99bc22017-02-02 01:31:30 -0800274 AddAttribute(fingerprint_attr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000275
276 // Calculate the CRC-32 for the message and insert it.
jbauchf1f87202016-03-30 06:43:37 -0700277 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000278 if (!Write(&buf))
279 return false;
280
281 int msg_len_for_crc32 = static_cast<int>(
282 buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200283 uint32_t c = rtc::ComputeCrc32(buf.Data(), msg_len_for_crc32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000284
285 // Insert the correct CRC-32, XORed with a constant, into the attribute.
286 fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
287 return true;
288}
289
jbauchf1f87202016-03-30 06:43:37 -0700290bool StunMessage::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000291 if (!buf->ReadUInt16(&type_))
292 return false;
293
294 if (type_ & 0x8000) {
295 // RTP and RTCP set the MSB of first byte, since first two bits are version,
296 // and version is always 2 (10). If set, this is not a STUN packet.
297 return false;
298 }
299
300 if (!buf->ReadUInt16(&length_))
301 return false;
302
303 std::string magic_cookie;
304 if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
305 return false;
306
307 std::string transaction_id;
308 if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
309 return false;
310
Peter Boström0c4e06b2015-10-07 12:23:21 +0200311 uint32_t magic_cookie_int =
312 *reinterpret_cast<const uint32_t*>(magic_cookie.data());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000313 if (rtc::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
314 // If magic cookie is invalid it means that the peer implements
315 // RFC3489 instead of RFC5389.
316 transaction_id.insert(0, magic_cookie);
317 }
nisseede5da42017-01-12 05:15:36 -0800318 RTC_DCHECK(IsValidTransactionId(transaction_id));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000319 transaction_id_ = transaction_id;
320
321 if (length_ != buf->Length())
322 return false;
323
zsteinad94c4c2017-03-06 13:36:05 -0800324 attrs_.resize(0);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000325
326 size_t rest = buf->Length() - length_;
327 while (buf->Length() > rest) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200328 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000329 if (!buf->ReadUInt16(&attr_type))
330 return false;
331 if (!buf->ReadUInt16(&attr_length))
332 return false;
333
Honghai Zhang3e024302016-09-22 09:52:16 -0700334 std::unique_ptr<StunAttribute> attr(
335 CreateAttribute(attr_type, attr_length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000336 if (!attr) {
337 // Skip any unknown or malformed attributes.
338 if ((attr_length % 4) != 0) {
339 attr_length += (4 - (attr_length % 4));
340 }
341 if (!buf->Consume(attr_length))
342 return false;
343 } else {
344 if (!attr->Read(buf))
345 return false;
zsteinad94c4c2017-03-06 13:36:05 -0800346 attrs_.push_back(std::move(attr));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000347 }
348 }
349
nisseede5da42017-01-12 05:15:36 -0800350 RTC_DCHECK(buf->Length() == rest);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000351 return true;
352}
353
jbauchf1f87202016-03-30 06:43:37 -0700354bool StunMessage::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000355 buf->WriteUInt16(type_);
356 buf->WriteUInt16(length_);
357 if (!IsLegacy())
358 buf->WriteUInt32(kStunMagicCookie);
359 buf->WriteString(transaction_id_);
360
zsteinad94c4c2017-03-06 13:36:05 -0800361 for (const auto& attr : attrs_) {
362 buf->WriteUInt16(attr->type());
363 buf->WriteUInt16(static_cast<uint16_t>(attr->length()));
364 if (!attr->Write(buf)) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000365 return false;
zsteinad94c4c2017-03-06 13:36:05 -0800366 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000367 }
368
369 return true;
370}
371
372StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
373 switch (type) {
374 case STUN_ATTR_MAPPED_ADDRESS: return STUN_VALUE_ADDRESS;
375 case STUN_ATTR_USERNAME: return STUN_VALUE_BYTE_STRING;
376 case STUN_ATTR_MESSAGE_INTEGRITY: return STUN_VALUE_BYTE_STRING;
377 case STUN_ATTR_ERROR_CODE: return STUN_VALUE_ERROR_CODE;
378 case STUN_ATTR_UNKNOWN_ATTRIBUTES: return STUN_VALUE_UINT16_LIST;
379 case STUN_ATTR_REALM: return STUN_VALUE_BYTE_STRING;
380 case STUN_ATTR_NONCE: return STUN_VALUE_BYTE_STRING;
381 case STUN_ATTR_XOR_MAPPED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
382 case STUN_ATTR_SOFTWARE: return STUN_VALUE_BYTE_STRING;
383 case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_ADDRESS;
384 case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000385 case STUN_ATTR_ORIGIN: return STUN_VALUE_BYTE_STRING;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000386 case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32;
387 default: return STUN_VALUE_UNKNOWN;
388 }
389}
390
391StunAttribute* StunMessage::CreateAttribute(int type, size_t length) /*const*/ {
392 StunAttributeValueType value_type = GetAttributeValueType(type);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200393 return StunAttribute::Create(value_type, type, static_cast<uint16_t>(length),
394 this);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000395}
396
397const StunAttribute* StunMessage::GetAttribute(int type) const {
zsteinad94c4c2017-03-06 13:36:05 -0800398 for (const auto& attr : attrs_) {
399 if (attr->type() == type) {
400 return attr.get();
401 }
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000402 }
403 return NULL;
404}
405
406bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
407 return transaction_id.size() == kStunTransactionIdLength ||
408 transaction_id.size() == kStunLegacyTransactionIdLength;
409}
410
411// StunAttribute
412
Peter Boström0c4e06b2015-10-07 12:23:21 +0200413StunAttribute::StunAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000414 : type_(type), length_(length) {
415}
416
jbauchf1f87202016-03-30 06:43:37 -0700417void StunAttribute::ConsumePadding(ByteBufferReader* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000418 int remainder = length_ % 4;
419 if (remainder > 0) {
420 buf->Consume(4 - remainder);
421 }
422}
423
jbauchf1f87202016-03-30 06:43:37 -0700424void StunAttribute::WritePadding(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000425 int remainder = length_ % 4;
426 if (remainder > 0) {
427 char zeroes[4] = {0};
428 buf->WriteBytes(zeroes, 4 - remainder);
429 }
430}
431
432StunAttribute* StunAttribute::Create(StunAttributeValueType value_type,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200433 uint16_t type,
434 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000435 StunMessage* owner) {
436 switch (value_type) {
437 case STUN_VALUE_ADDRESS:
438 return new StunAddressAttribute(type, length);
439 case STUN_VALUE_XOR_ADDRESS:
440 return new StunXorAddressAttribute(type, length, owner);
441 case STUN_VALUE_UINT32:
442 return new StunUInt32Attribute(type);
443 case STUN_VALUE_UINT64:
444 return new StunUInt64Attribute(type);
445 case STUN_VALUE_BYTE_STRING:
446 return new StunByteStringAttribute(type, length);
447 case STUN_VALUE_ERROR_CODE:
448 return new StunErrorCodeAttribute(type, length);
449 case STUN_VALUE_UINT16_LIST:
450 return new StunUInt16ListAttribute(type, length);
451 default:
452 return NULL;
453 }
454}
455
Peter Boström0c4e06b2015-10-07 12:23:21 +0200456StunAddressAttribute* StunAttribute::CreateAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000457 return new StunAddressAttribute(type, 0);
458}
459
Peter Boström0c4e06b2015-10-07 12:23:21 +0200460StunXorAddressAttribute* StunAttribute::CreateXorAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000461 return new StunXorAddressAttribute(type, 0, NULL);
462}
463
Peter Boström0c4e06b2015-10-07 12:23:21 +0200464StunUInt64Attribute* StunAttribute::CreateUInt64(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000465 return new StunUInt64Attribute(type);
466}
467
Peter Boström0c4e06b2015-10-07 12:23:21 +0200468StunUInt32Attribute* StunAttribute::CreateUInt32(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000469 return new StunUInt32Attribute(type);
470}
471
Peter Boström0c4e06b2015-10-07 12:23:21 +0200472StunByteStringAttribute* StunAttribute::CreateByteString(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000473 return new StunByteStringAttribute(type, 0);
474}
475
476StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
477 return new StunErrorCodeAttribute(
478 STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
479}
480
481StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
482 return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
483}
484
Peter Boström0c4e06b2015-10-07 12:23:21 +0200485StunAddressAttribute::StunAddressAttribute(uint16_t type,
486 const rtc::SocketAddress& addr)
487 : StunAttribute(type, 0) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000488 SetAddress(addr);
489}
490
Peter Boström0c4e06b2015-10-07 12:23:21 +0200491StunAddressAttribute::StunAddressAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000492 : StunAttribute(type, length) {
493}
494
jbauchf1f87202016-03-30 06:43:37 -0700495bool StunAddressAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200496 uint8_t dummy;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000497 if (!buf->ReadUInt8(&dummy))
498 return false;
499
Peter Boström0c4e06b2015-10-07 12:23:21 +0200500 uint8_t stun_family;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000501 if (!buf->ReadUInt8(&stun_family)) {
502 return false;
503 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200504 uint16_t port;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000505 if (!buf->ReadUInt16(&port))
506 return false;
507 if (stun_family == STUN_ADDRESS_IPV4) {
508 in_addr v4addr;
509 if (length() != SIZE_IP4) {
510 return false;
511 }
512 if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
513 return false;
514 }
515 rtc::IPAddress ipaddr(v4addr);
516 SetAddress(rtc::SocketAddress(ipaddr, port));
517 } else if (stun_family == STUN_ADDRESS_IPV6) {
518 in6_addr v6addr;
519 if (length() != SIZE_IP6) {
520 return false;
521 }
522 if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
523 return false;
524 }
525 rtc::IPAddress ipaddr(v6addr);
526 SetAddress(rtc::SocketAddress(ipaddr, port));
527 } else {
528 return false;
529 }
530 return true;
531}
532
jbauchf1f87202016-03-30 06:43:37 -0700533bool StunAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000534 StunAddressFamily address_family = family();
535 if (address_family == STUN_ADDRESS_UNDEF) {
536 LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
537 return false;
538 }
539 buf->WriteUInt8(0);
540 buf->WriteUInt8(address_family);
541 buf->WriteUInt16(address_.port());
542 switch (address_.family()) {
543 case AF_INET: {
544 in_addr v4addr = address_.ipaddr().ipv4_address();
545 buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
546 break;
547 }
548 case AF_INET6: {
549 in6_addr v6addr = address_.ipaddr().ipv6_address();
550 buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
551 break;
552 }
553 }
554 return true;
555}
556
Peter Boström0c4e06b2015-10-07 12:23:21 +0200557StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
558 const rtc::SocketAddress& addr)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000559 : StunAddressAttribute(type, addr), owner_(NULL) {
560}
561
Peter Boström0c4e06b2015-10-07 12:23:21 +0200562StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
563 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000564 StunMessage* owner)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200565 : StunAddressAttribute(type, length), owner_(owner) {
566}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000567
568rtc::IPAddress StunXorAddressAttribute::GetXoredIP() const {
569 if (owner_) {
570 rtc::IPAddress ip = ipaddr();
571 switch (ip.family()) {
572 case AF_INET: {
573 in_addr v4addr = ip.ipv4_address();
574 v4addr.s_addr =
575 (v4addr.s_addr ^ rtc::HostToNetwork32(kStunMagicCookie));
576 return rtc::IPAddress(v4addr);
577 }
578 case AF_INET6: {
579 in6_addr v6addr = ip.ipv6_address();
580 const std::string& transaction_id = owner_->transaction_id();
581 if (transaction_id.length() == kStunTransactionIdLength) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200582 uint32_t transactionid_as_ints[3];
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000583 memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
584 transaction_id.length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200585 uint32_t* ip_as_ints = reinterpret_cast<uint32_t*>(&v6addr.s6_addr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000586 // Transaction ID is in network byte order, but magic cookie
587 // is stored in host byte order.
588 ip_as_ints[0] =
589 (ip_as_ints[0] ^ rtc::HostToNetwork32(kStunMagicCookie));
590 ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
591 ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
592 ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
593 return rtc::IPAddress(v6addr);
594 }
595 break;
596 }
597 }
598 }
599 // Invalid ip family or transaction ID, or missing owner.
600 // Return an AF_UNSPEC address.
601 return rtc::IPAddress();
602}
603
jbauchf1f87202016-03-30 06:43:37 -0700604bool StunXorAddressAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000605 if (!StunAddressAttribute::Read(buf))
606 return false;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200607 uint16_t xoredport = port() ^ (kStunMagicCookie >> 16);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000608 rtc::IPAddress xored_ip = GetXoredIP();
609 SetAddress(rtc::SocketAddress(xored_ip, xoredport));
610 return true;
611}
612
jbauchf1f87202016-03-30 06:43:37 -0700613bool StunXorAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000614 StunAddressFamily address_family = family();
615 if (address_family == STUN_ADDRESS_UNDEF) {
616 LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
617 return false;
618 }
619 rtc::IPAddress xored_ip = GetXoredIP();
620 if (xored_ip.family() == AF_UNSPEC) {
621 return false;
622 }
623 buf->WriteUInt8(0);
624 buf->WriteUInt8(family());
625 buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
626 switch (xored_ip.family()) {
627 case AF_INET: {
628 in_addr v4addr = xored_ip.ipv4_address();
629 buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
630 break;
631 }
632 case AF_INET6: {
633 in6_addr v6addr = xored_ip.ipv6_address();
634 buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
635 break;
636 }
637 }
638 return true;
639}
640
Peter Boström0c4e06b2015-10-07 12:23:21 +0200641StunUInt32Attribute::StunUInt32Attribute(uint16_t type, uint32_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000642 : StunAttribute(type, SIZE), bits_(value) {
643}
644
Peter Boström0c4e06b2015-10-07 12:23:21 +0200645StunUInt32Attribute::StunUInt32Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000646 : StunAttribute(type, SIZE), bits_(0) {
647}
648
649bool StunUInt32Attribute::GetBit(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800650 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000651 return static_cast<bool>((bits_ >> index) & 0x1);
652}
653
654void StunUInt32Attribute::SetBit(size_t index, bool value) {
nisseede5da42017-01-12 05:15:36 -0800655 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000656 bits_ &= ~(1 << index);
657 bits_ |= value ? (1 << index) : 0;
658}
659
jbauchf1f87202016-03-30 06:43:37 -0700660bool StunUInt32Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000661 if (length() != SIZE || !buf->ReadUInt32(&bits_))
662 return false;
663 return true;
664}
665
jbauchf1f87202016-03-30 06:43:37 -0700666bool StunUInt32Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000667 buf->WriteUInt32(bits_);
668 return true;
669}
670
Peter Boström0c4e06b2015-10-07 12:23:21 +0200671StunUInt64Attribute::StunUInt64Attribute(uint16_t type, uint64_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000672 : StunAttribute(type, SIZE), bits_(value) {
673}
674
Peter Boström0c4e06b2015-10-07 12:23:21 +0200675StunUInt64Attribute::StunUInt64Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000676 : StunAttribute(type, SIZE), bits_(0) {
677}
678
jbauchf1f87202016-03-30 06:43:37 -0700679bool StunUInt64Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000680 if (length() != SIZE || !buf->ReadUInt64(&bits_))
681 return false;
682 return true;
683}
684
jbauchf1f87202016-03-30 06:43:37 -0700685bool StunUInt64Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000686 buf->WriteUInt64(bits_);
687 return true;
688}
689
Peter Boström0c4e06b2015-10-07 12:23:21 +0200690StunByteStringAttribute::StunByteStringAttribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000691 : StunAttribute(type, 0), bytes_(NULL) {
692}
693
Peter Boström0c4e06b2015-10-07 12:23:21 +0200694StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000695 const std::string& str)
696 : StunAttribute(type, 0), bytes_(NULL) {
697 CopyBytes(str.c_str(), str.size());
698}
699
Peter Boström0c4e06b2015-10-07 12:23:21 +0200700StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000701 const void* bytes,
702 size_t length)
703 : StunAttribute(type, 0), bytes_(NULL) {
704 CopyBytes(bytes, length);
705}
706
Peter Boström0c4e06b2015-10-07 12:23:21 +0200707StunByteStringAttribute::StunByteStringAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000708 : StunAttribute(type, length), bytes_(NULL) {
709}
710
711StunByteStringAttribute::~StunByteStringAttribute() {
712 delete [] bytes_;
713}
714
715void StunByteStringAttribute::CopyBytes(const char* bytes) {
716 CopyBytes(bytes, strlen(bytes));
717}
718
719void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
720 char* new_bytes = new char[length];
721 memcpy(new_bytes, bytes, length);
722 SetBytes(new_bytes, length);
723}
724
Peter Boström0c4e06b2015-10-07 12:23:21 +0200725uint8_t StunByteStringAttribute::GetByte(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800726 RTC_DCHECK(bytes_ != NULL);
727 RTC_DCHECK(index < length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200728 return static_cast<uint8_t>(bytes_[index]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000729}
730
Peter Boström0c4e06b2015-10-07 12:23:21 +0200731void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
nisseede5da42017-01-12 05:15:36 -0800732 RTC_DCHECK(bytes_ != NULL);
733 RTC_DCHECK(index < length());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000734 bytes_[index] = value;
735}
736
jbauchf1f87202016-03-30 06:43:37 -0700737bool StunByteStringAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000738 bytes_ = new char[length()];
739 if (!buf->ReadBytes(bytes_, length())) {
740 return false;
741 }
742
743 ConsumePadding(buf);
744 return true;
745}
746
jbauchf1f87202016-03-30 06:43:37 -0700747bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000748 buf->WriteBytes(bytes_, length());
749 WritePadding(buf);
750 return true;
751}
752
753void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
754 delete [] bytes_;
755 bytes_ = bytes;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200756 SetLength(static_cast<uint16_t>(length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000757}
758
Peter Boström0c4e06b2015-10-07 12:23:21 +0200759StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type,
760 int code,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000761 const std::string& reason)
762 : StunAttribute(type, 0) {
763 SetCode(code);
764 SetReason(reason);
765}
766
Peter Boström0c4e06b2015-10-07 12:23:21 +0200767StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000768 : StunAttribute(type, length), class_(0), number_(0) {
769}
770
771StunErrorCodeAttribute::~StunErrorCodeAttribute() {
772}
773
774int StunErrorCodeAttribute::code() const {
775 return class_ * 100 + number_;
776}
777
778void StunErrorCodeAttribute::SetCode(int code) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200779 class_ = static_cast<uint8_t>(code / 100);
780 number_ = static_cast<uint8_t>(code % 100);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000781}
782
783void StunErrorCodeAttribute::SetReason(const std::string& reason) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200784 SetLength(MIN_SIZE + static_cast<uint16_t>(reason.size()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000785 reason_ = reason;
786}
787
jbauchf1f87202016-03-30 06:43:37 -0700788bool StunErrorCodeAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200789 uint32_t val;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000790 if (length() < MIN_SIZE || !buf->ReadUInt32(&val))
791 return false;
792
793 if ((val >> 11) != 0)
794 LOG(LS_ERROR) << "error-code bits not zero";
795
796 class_ = ((val >> 8) & 0x7);
797 number_ = (val & 0xff);
798
799 if (!buf->ReadString(&reason_, length() - 4))
800 return false;
801
802 ConsumePadding(buf);
803 return true;
804}
805
jbauchf1f87202016-03-30 06:43:37 -0700806bool StunErrorCodeAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000807 buf->WriteUInt32(class_ << 8 | number_);
808 buf->WriteString(reason_);
809 WritePadding(buf);
810 return true;
811}
812
Peter Boström0c4e06b2015-10-07 12:23:21 +0200813StunUInt16ListAttribute::StunUInt16ListAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000814 : StunAttribute(type, length) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200815 attr_types_ = new std::vector<uint16_t>();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000816}
817
818StunUInt16ListAttribute::~StunUInt16ListAttribute() {
819 delete attr_types_;
820}
821
822size_t StunUInt16ListAttribute::Size() const {
823 return attr_types_->size();
824}
825
Peter Boström0c4e06b2015-10-07 12:23:21 +0200826uint16_t StunUInt16ListAttribute::GetType(int index) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000827 return (*attr_types_)[index];
828}
829
Peter Boström0c4e06b2015-10-07 12:23:21 +0200830void StunUInt16ListAttribute::SetType(int index, uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000831 (*attr_types_)[index] = value;
832}
833
Peter Boström0c4e06b2015-10-07 12:23:21 +0200834void StunUInt16ListAttribute::AddType(uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000835 attr_types_->push_back(value);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200836 SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000837}
838
jbauchf1f87202016-03-30 06:43:37 -0700839bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000840 if (length() % 2)
841 return false;
842
843 for (size_t i = 0; i < length() / 2; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200844 uint16_t attr;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000845 if (!buf->ReadUInt16(&attr))
846 return false;
847 attr_types_->push_back(attr);
848 }
849 // Padding of these attributes is done in RFC 5389 style. This is
850 // slightly different from RFC3489, but it shouldn't be important.
851 // RFC3489 pads out to a 32 bit boundary by duplicating one of the
852 // entries in the list (not necessarily the last one - it's unspecified).
853 // RFC5389 pads on the end, and the bytes are always ignored.
854 ConsumePadding(buf);
855 return true;
856}
857
jbauchf1f87202016-03-30 06:43:37 -0700858bool StunUInt16ListAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000859 for (size_t i = 0; i < attr_types_->size(); ++i) {
860 buf->WriteUInt16((*attr_types_)[i]);
861 }
862 WritePadding(buf);
863 return true;
864}
865
866int GetStunSuccessResponseType(int req_type) {
867 return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
868}
869
870int GetStunErrorResponseType(int req_type) {
871 return IsStunRequestType(req_type) ? (req_type | 0x110) : -1;
872}
873
874bool IsStunRequestType(int msg_type) {
875 return ((msg_type & kStunTypeMask) == 0x000);
876}
877
878bool IsStunIndicationType(int msg_type) {
879 return ((msg_type & kStunTypeMask) == 0x010);
880}
881
882bool IsStunSuccessResponseType(int msg_type) {
883 return ((msg_type & kStunTypeMask) == 0x100);
884}
885
886bool IsStunErrorResponseType(int msg_type) {
887 return ((msg_type & kStunTypeMask) == 0x110);
888}
889
890bool ComputeStunCredentialHash(const std::string& username,
891 const std::string& realm,
892 const std::string& password,
893 std::string* hash) {
894 // http://tools.ietf.org/html/rfc5389#section-15.4
895 // long-term credentials will be calculated using the key and key is
896 // key = MD5(username ":" realm ":" SASLprep(password))
897 std::string input = username;
898 input += ':';
899 input += realm;
900 input += ':';
901 input += password;
902
903 char digest[rtc::MessageDigest::kMaxSize];
904 size_t size = rtc::ComputeDigest(
905 rtc::DIGEST_MD5, input.c_str(), input.size(),
906 digest, sizeof(digest));
907 if (size == 0) {
908 return false;
909 }
910
911 *hash = std::string(digest, size);
912 return true;
913}
914
915} // namespace cricket