blob: 3d11c71cd3f3c59a8e144cd0557459958bcb9392 [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/common.h"
20#include "webrtc/base/crc32.h"
21#include "webrtc/base/logging.h"
22#include "webrtc/base/messagedigest.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000023#include "webrtc/base/stringencode.h"
24
jbauchf1f87202016-03-30 06:43:37 -070025using rtc::ByteBufferReader;
26using rtc::ByteBufferWriter;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000027
28namespace cricket {
29
30const char STUN_ERROR_REASON_TRY_ALTERNATE_SERVER[] = "Try Alternate Server";
31const char STUN_ERROR_REASON_BAD_REQUEST[] = "Bad Request";
32const char STUN_ERROR_REASON_UNAUTHORIZED[] = "Unauthorized";
33const char STUN_ERROR_REASON_FORBIDDEN[] = "Forbidden";
34const char STUN_ERROR_REASON_STALE_CREDENTIALS[] = "Stale Credentials";
35const char STUN_ERROR_REASON_ALLOCATION_MISMATCH[] = "Allocation Mismatch";
36const char STUN_ERROR_REASON_STALE_NONCE[] = "Stale Nonce";
37const char STUN_ERROR_REASON_WRONG_CREDENTIALS[] = "Wrong Credentials";
38const char STUN_ERROR_REASON_UNSUPPORTED_PROTOCOL[] = "Unsupported Protocol";
39const char STUN_ERROR_REASON_ROLE_CONFLICT[] = "Role Conflict";
40const char STUN_ERROR_REASON_SERVER_ERROR[] = "Server Error";
41
42const char TURN_MAGIC_COOKIE_VALUE[] = { '\x72', '\xC6', '\x4B', '\xC6' };
43const char EMPTY_TRANSACTION_ID[] = "0000000000000000";
Peter Boström0c4e06b2015-10-07 12:23:21 +020044const uint32_t STUN_FINGERPRINT_XOR_VALUE = 0x5354554E;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000045
46// StunMessage
47
48StunMessage::StunMessage()
49 : type_(0),
50 length_(0),
51 transaction_id_(EMPTY_TRANSACTION_ID) {
nisseede5da42017-01-12 05:15:36 -080052 RTC_DCHECK(IsValidTransactionId(transaction_id_));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000053 attrs_ = new std::vector<StunAttribute*>();
54}
55
56StunMessage::~StunMessage() {
57 for (size_t i = 0; i < attrs_->size(); i++)
58 delete (*attrs_)[i];
59 delete attrs_;
60}
61
62bool StunMessage::IsLegacy() const {
63 if (transaction_id_.size() == kStunLegacyTransactionIdLength)
64 return true;
nisseede5da42017-01-12 05:15:36 -080065 RTC_DCHECK(transaction_id_.size() == kStunTransactionIdLength);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000066 return false;
67}
68
69bool StunMessage::SetTransactionID(const std::string& str) {
70 if (!IsValidTransactionId(str)) {
71 return false;
72 }
73 transaction_id_ = str;
74 return true;
75}
76
77bool StunMessage::AddAttribute(StunAttribute* attr) {
78 // Fail any attributes that aren't valid for this type of message.
79 if (attr->value_type() != GetAttributeValueType(attr->type())) {
80 return false;
81 }
82 attrs_->push_back(attr);
83 attr->SetOwner(this);
84 size_t attr_length = attr->length();
85 if (attr_length % 4 != 0) {
86 attr_length += (4 - (attr_length % 4));
87 }
Peter Boström0c4e06b2015-10-07 12:23:21 +020088 length_ += static_cast<uint16_t>(attr_length + 4);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000089 return true;
90}
91
92const StunAddressAttribute* StunMessage::GetAddress(int type) const {
93 switch (type) {
94 case STUN_ATTR_MAPPED_ADDRESS: {
95 // Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
96 // missing.
97 const StunAttribute* mapped_address =
98 GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
99 if (!mapped_address)
100 mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
101 return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
102 }
103
104 default:
105 return static_cast<const StunAddressAttribute*>(GetAttribute(type));
106 }
107}
108
109const StunUInt32Attribute* StunMessage::GetUInt32(int type) const {
110 return static_cast<const StunUInt32Attribute*>(GetAttribute(type));
111}
112
113const StunUInt64Attribute* StunMessage::GetUInt64(int type) const {
114 return static_cast<const StunUInt64Attribute*>(GetAttribute(type));
115}
116
117const StunByteStringAttribute* StunMessage::GetByteString(int type) const {
118 return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
119}
120
121const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
122 return static_cast<const StunErrorCodeAttribute*>(
123 GetAttribute(STUN_ATTR_ERROR_CODE));
124}
125
126const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
127 return static_cast<const StunUInt16ListAttribute*>(
128 GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
129}
130
131// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
132// procedure outlined in RFC 5389, section 15.4.
133bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size,
134 const std::string& password) {
135 // Verifying the size of the message.
katrielce4bda242016-06-09 08:45:45 -0700136 if ((size % 4) != 0 || size < kStunHeaderSize) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000137 return false;
138 }
139
140 // Getting the message length from the STUN header.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200141 uint16_t msg_length = rtc::GetBE16(&data[2]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000142 if (size != (msg_length + kStunHeaderSize)) {
143 return false;
144 }
145
146 // Finding Message Integrity attribute in stun message.
147 size_t current_pos = kStunHeaderSize;
148 bool has_message_integrity_attr = false;
katrielc1a206102016-06-20 05:13:16 -0700149 while (current_pos + 4 <= size) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200150 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000151 // Getting attribute type and length.
152 attr_type = rtc::GetBE16(&data[current_pos]);
153 attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
154
155 // If M-I, sanity check it, and break out.
156 if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
157 if (attr_length != kStunMessageIntegritySize ||
katrielc1a206102016-06-20 05:13:16 -0700158 current_pos + sizeof(attr_type) + sizeof(attr_length) + attr_length >
159 size) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000160 return false;
161 }
162 has_message_integrity_attr = true;
163 break;
164 }
165
166 // Otherwise, skip to the next attribute.
167 current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
168 if ((attr_length % 4) != 0) {
169 current_pos += (4 - (attr_length % 4));
170 }
171 }
172
173 if (!has_message_integrity_attr) {
174 return false;
175 }
176
177 // Getting length of the message to calculate Message Integrity.
178 size_t mi_pos = current_pos;
kwiberg3ec46792016-04-27 07:22:53 -0700179 std::unique_ptr<char[]> temp_data(new char[current_pos]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000180 memcpy(temp_data.get(), data, current_pos);
181 if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
182 // Stun message has other attributes after message integrity.
183 // Adjust the length parameter in stun message to calculate HMAC.
184 size_t extra_offset = size -
185 (mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
186 size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
187
188 // Writing new length of the STUN message @ Message Length in temp buffer.
189 // 0 1 2 3
190 // 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
191 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
192 // |0 0| STUN Message Type | Message Length |
193 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Peter Boström0c4e06b2015-10-07 12:23:21 +0200194 rtc::SetBE16(temp_data.get() + 2, static_cast<uint16_t>(new_adjusted_len));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000195 }
196
197 char hmac[kStunMessageIntegritySize];
198 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
199 password.c_str(), password.size(),
200 temp_data.get(), mi_pos,
201 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800202 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000203 if (ret != sizeof(hmac))
204 return false;
205
206 // Comparing the calculated HMAC with the one present in the message.
207 return memcmp(data + current_pos + kStunAttributeHeaderSize,
208 hmac,
209 sizeof(hmac)) == 0;
210}
211
212bool StunMessage::AddMessageIntegrity(const std::string& password) {
213 return AddMessageIntegrity(password.c_str(), password.size());
214}
215
216bool StunMessage::AddMessageIntegrity(const char* key,
217 size_t keylen) {
218 // Add the attribute with a dummy value. Since this is a known attribute, it
219 // can't fail.
220 StunByteStringAttribute* msg_integrity_attr =
221 new StunByteStringAttribute(STUN_ATTR_MESSAGE_INTEGRITY,
222 std::string(kStunMessageIntegritySize, '0'));
223 VERIFY(AddAttribute(msg_integrity_attr));
224
225 // Calculate the HMAC for the message.
jbauchf1f87202016-03-30 06:43:37 -0700226 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000227 if (!Write(&buf))
228 return false;
229
230 int msg_len_for_hmac = static_cast<int>(
231 buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
232 char hmac[kStunMessageIntegritySize];
233 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
234 key, keylen,
235 buf.Data(), msg_len_for_hmac,
236 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800237 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000238 if (ret != sizeof(hmac)) {
239 LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
240 << "has dummy value.";
241 return false;
242 }
243
244 // Insert correct HMAC into the attribute.
245 msg_integrity_attr->CopyBytes(hmac, sizeof(hmac));
246 return true;
247}
248
249// Verifies a message is in fact a STUN message, by performing the checks
250// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
251// in section 15.5.
252bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
253 // Check the message length.
254 size_t fingerprint_attr_size =
255 kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
256 if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
257 return false;
258
259 // Skip the rest if the magic cookie isn't present.
260 const char* magic_cookie =
261 data + kStunTransactionIdOffset - kStunMagicCookieLength;
262 if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
263 return false;
264
265 // Check the fingerprint type and length.
266 const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
267 if (rtc::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
Peter Boström0c4e06b2015-10-07 12:23:21 +0200268 rtc::GetBE16(fingerprint_attr_data + sizeof(uint16_t)) !=
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000269 StunUInt32Attribute::SIZE)
270 return false;
271
272 // Check the fingerprint value.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200273 uint32_t fingerprint =
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000274 rtc::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
275 return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
276 rtc::ComputeCrc32(data, size - fingerprint_attr_size));
277}
278
279bool StunMessage::AddFingerprint() {
280 // Add the attribute with a dummy value. Since this is a known attribute,
281 // it can't fail.
282 StunUInt32Attribute* fingerprint_attr =
283 new StunUInt32Attribute(STUN_ATTR_FINGERPRINT, 0);
284 VERIFY(AddAttribute(fingerprint_attr));
285
286 // Calculate the CRC-32 for the message and insert it.
jbauchf1f87202016-03-30 06:43:37 -0700287 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000288 if (!Write(&buf))
289 return false;
290
291 int msg_len_for_crc32 = static_cast<int>(
292 buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200293 uint32_t c = rtc::ComputeCrc32(buf.Data(), msg_len_for_crc32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000294
295 // Insert the correct CRC-32, XORed with a constant, into the attribute.
296 fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
297 return true;
298}
299
jbauchf1f87202016-03-30 06:43:37 -0700300bool StunMessage::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000301 if (!buf->ReadUInt16(&type_))
302 return false;
303
304 if (type_ & 0x8000) {
305 // RTP and RTCP set the MSB of first byte, since first two bits are version,
306 // and version is always 2 (10). If set, this is not a STUN packet.
307 return false;
308 }
309
310 if (!buf->ReadUInt16(&length_))
311 return false;
312
313 std::string magic_cookie;
314 if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
315 return false;
316
317 std::string transaction_id;
318 if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
319 return false;
320
Peter Boström0c4e06b2015-10-07 12:23:21 +0200321 uint32_t magic_cookie_int =
322 *reinterpret_cast<const uint32_t*>(magic_cookie.data());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000323 if (rtc::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
324 // If magic cookie is invalid it means that the peer implements
325 // RFC3489 instead of RFC5389.
326 transaction_id.insert(0, magic_cookie);
327 }
nisseede5da42017-01-12 05:15:36 -0800328 RTC_DCHECK(IsValidTransactionId(transaction_id));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000329 transaction_id_ = transaction_id;
330
331 if (length_ != buf->Length())
332 return false;
333
334 attrs_->resize(0);
335
336 size_t rest = buf->Length() - length_;
337 while (buf->Length() > rest) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200338 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000339 if (!buf->ReadUInt16(&attr_type))
340 return false;
341 if (!buf->ReadUInt16(&attr_length))
342 return false;
343
Honghai Zhang3e024302016-09-22 09:52:16 -0700344 std::unique_ptr<StunAttribute> attr(
345 CreateAttribute(attr_type, attr_length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000346 if (!attr) {
347 // Skip any unknown or malformed attributes.
348 if ((attr_length % 4) != 0) {
349 attr_length += (4 - (attr_length % 4));
350 }
351 if (!buf->Consume(attr_length))
352 return false;
353 } else {
354 if (!attr->Read(buf))
355 return false;
Honghai Zhang3e024302016-09-22 09:52:16 -0700356 // TODO(honghaiz): Change |attrs_| to be a vector of unique_ptrs.
357 attrs_->push_back(attr.release());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000358 }
359 }
360
nisseede5da42017-01-12 05:15:36 -0800361 RTC_DCHECK(buf->Length() == rest);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000362 return true;
363}
364
jbauchf1f87202016-03-30 06:43:37 -0700365bool StunMessage::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000366 buf->WriteUInt16(type_);
367 buf->WriteUInt16(length_);
368 if (!IsLegacy())
369 buf->WriteUInt32(kStunMagicCookie);
370 buf->WriteString(transaction_id_);
371
372 for (size_t i = 0; i < attrs_->size(); ++i) {
373 buf->WriteUInt16((*attrs_)[i]->type());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200374 buf->WriteUInt16(static_cast<uint16_t>((*attrs_)[i]->length()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000375 if (!(*attrs_)[i]->Write(buf))
376 return false;
377 }
378
379 return true;
380}
381
382StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
383 switch (type) {
384 case STUN_ATTR_MAPPED_ADDRESS: return STUN_VALUE_ADDRESS;
385 case STUN_ATTR_USERNAME: return STUN_VALUE_BYTE_STRING;
386 case STUN_ATTR_MESSAGE_INTEGRITY: return STUN_VALUE_BYTE_STRING;
387 case STUN_ATTR_ERROR_CODE: return STUN_VALUE_ERROR_CODE;
388 case STUN_ATTR_UNKNOWN_ATTRIBUTES: return STUN_VALUE_UINT16_LIST;
389 case STUN_ATTR_REALM: return STUN_VALUE_BYTE_STRING;
390 case STUN_ATTR_NONCE: return STUN_VALUE_BYTE_STRING;
391 case STUN_ATTR_XOR_MAPPED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
392 case STUN_ATTR_SOFTWARE: return STUN_VALUE_BYTE_STRING;
393 case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_ADDRESS;
394 case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000395 case STUN_ATTR_ORIGIN: return STUN_VALUE_BYTE_STRING;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000396 case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32;
397 default: return STUN_VALUE_UNKNOWN;
398 }
399}
400
401StunAttribute* StunMessage::CreateAttribute(int type, size_t length) /*const*/ {
402 StunAttributeValueType value_type = GetAttributeValueType(type);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200403 return StunAttribute::Create(value_type, type, static_cast<uint16_t>(length),
404 this);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000405}
406
407const StunAttribute* StunMessage::GetAttribute(int type) const {
408 for (size_t i = 0; i < attrs_->size(); ++i) {
409 if ((*attrs_)[i]->type() == type)
410 return (*attrs_)[i];
411 }
412 return NULL;
413}
414
415bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
416 return transaction_id.size() == kStunTransactionIdLength ||
417 transaction_id.size() == kStunLegacyTransactionIdLength;
418}
419
420// StunAttribute
421
Peter Boström0c4e06b2015-10-07 12:23:21 +0200422StunAttribute::StunAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000423 : type_(type), length_(length) {
424}
425
jbauchf1f87202016-03-30 06:43:37 -0700426void StunAttribute::ConsumePadding(ByteBufferReader* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000427 int remainder = length_ % 4;
428 if (remainder > 0) {
429 buf->Consume(4 - remainder);
430 }
431}
432
jbauchf1f87202016-03-30 06:43:37 -0700433void StunAttribute::WritePadding(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000434 int remainder = length_ % 4;
435 if (remainder > 0) {
436 char zeroes[4] = {0};
437 buf->WriteBytes(zeroes, 4 - remainder);
438 }
439}
440
441StunAttribute* StunAttribute::Create(StunAttributeValueType value_type,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200442 uint16_t type,
443 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000444 StunMessage* owner) {
445 switch (value_type) {
446 case STUN_VALUE_ADDRESS:
447 return new StunAddressAttribute(type, length);
448 case STUN_VALUE_XOR_ADDRESS:
449 return new StunXorAddressAttribute(type, length, owner);
450 case STUN_VALUE_UINT32:
451 return new StunUInt32Attribute(type);
452 case STUN_VALUE_UINT64:
453 return new StunUInt64Attribute(type);
454 case STUN_VALUE_BYTE_STRING:
455 return new StunByteStringAttribute(type, length);
456 case STUN_VALUE_ERROR_CODE:
457 return new StunErrorCodeAttribute(type, length);
458 case STUN_VALUE_UINT16_LIST:
459 return new StunUInt16ListAttribute(type, length);
460 default:
461 return NULL;
462 }
463}
464
Peter Boström0c4e06b2015-10-07 12:23:21 +0200465StunAddressAttribute* StunAttribute::CreateAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000466 return new StunAddressAttribute(type, 0);
467}
468
Peter Boström0c4e06b2015-10-07 12:23:21 +0200469StunXorAddressAttribute* StunAttribute::CreateXorAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000470 return new StunXorAddressAttribute(type, 0, NULL);
471}
472
Peter Boström0c4e06b2015-10-07 12:23:21 +0200473StunUInt64Attribute* StunAttribute::CreateUInt64(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000474 return new StunUInt64Attribute(type);
475}
476
Peter Boström0c4e06b2015-10-07 12:23:21 +0200477StunUInt32Attribute* StunAttribute::CreateUInt32(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000478 return new StunUInt32Attribute(type);
479}
480
Peter Boström0c4e06b2015-10-07 12:23:21 +0200481StunByteStringAttribute* StunAttribute::CreateByteString(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000482 return new StunByteStringAttribute(type, 0);
483}
484
485StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
486 return new StunErrorCodeAttribute(
487 STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
488}
489
490StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
491 return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
492}
493
Peter Boström0c4e06b2015-10-07 12:23:21 +0200494StunAddressAttribute::StunAddressAttribute(uint16_t type,
495 const rtc::SocketAddress& addr)
496 : StunAttribute(type, 0) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000497 SetAddress(addr);
498}
499
Peter Boström0c4e06b2015-10-07 12:23:21 +0200500StunAddressAttribute::StunAddressAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000501 : StunAttribute(type, length) {
502}
503
jbauchf1f87202016-03-30 06:43:37 -0700504bool StunAddressAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200505 uint8_t dummy;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000506 if (!buf->ReadUInt8(&dummy))
507 return false;
508
Peter Boström0c4e06b2015-10-07 12:23:21 +0200509 uint8_t stun_family;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000510 if (!buf->ReadUInt8(&stun_family)) {
511 return false;
512 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200513 uint16_t port;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000514 if (!buf->ReadUInt16(&port))
515 return false;
516 if (stun_family == STUN_ADDRESS_IPV4) {
517 in_addr v4addr;
518 if (length() != SIZE_IP4) {
519 return false;
520 }
521 if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
522 return false;
523 }
524 rtc::IPAddress ipaddr(v4addr);
525 SetAddress(rtc::SocketAddress(ipaddr, port));
526 } else if (stun_family == STUN_ADDRESS_IPV6) {
527 in6_addr v6addr;
528 if (length() != SIZE_IP6) {
529 return false;
530 }
531 if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
532 return false;
533 }
534 rtc::IPAddress ipaddr(v6addr);
535 SetAddress(rtc::SocketAddress(ipaddr, port));
536 } else {
537 return false;
538 }
539 return true;
540}
541
jbauchf1f87202016-03-30 06:43:37 -0700542bool StunAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000543 StunAddressFamily address_family = family();
544 if (address_family == STUN_ADDRESS_UNDEF) {
545 LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
546 return false;
547 }
548 buf->WriteUInt8(0);
549 buf->WriteUInt8(address_family);
550 buf->WriteUInt16(address_.port());
551 switch (address_.family()) {
552 case AF_INET: {
553 in_addr v4addr = address_.ipaddr().ipv4_address();
554 buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
555 break;
556 }
557 case AF_INET6: {
558 in6_addr v6addr = address_.ipaddr().ipv6_address();
559 buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
560 break;
561 }
562 }
563 return true;
564}
565
Peter Boström0c4e06b2015-10-07 12:23:21 +0200566StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
567 const rtc::SocketAddress& addr)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000568 : StunAddressAttribute(type, addr), owner_(NULL) {
569}
570
Peter Boström0c4e06b2015-10-07 12:23:21 +0200571StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
572 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000573 StunMessage* owner)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200574 : StunAddressAttribute(type, length), owner_(owner) {
575}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000576
577rtc::IPAddress StunXorAddressAttribute::GetXoredIP() const {
578 if (owner_) {
579 rtc::IPAddress ip = ipaddr();
580 switch (ip.family()) {
581 case AF_INET: {
582 in_addr v4addr = ip.ipv4_address();
583 v4addr.s_addr =
584 (v4addr.s_addr ^ rtc::HostToNetwork32(kStunMagicCookie));
585 return rtc::IPAddress(v4addr);
586 }
587 case AF_INET6: {
588 in6_addr v6addr = ip.ipv6_address();
589 const std::string& transaction_id = owner_->transaction_id();
590 if (transaction_id.length() == kStunTransactionIdLength) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200591 uint32_t transactionid_as_ints[3];
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000592 memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
593 transaction_id.length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200594 uint32_t* ip_as_ints = reinterpret_cast<uint32_t*>(&v6addr.s6_addr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000595 // Transaction ID is in network byte order, but magic cookie
596 // is stored in host byte order.
597 ip_as_ints[0] =
598 (ip_as_ints[0] ^ rtc::HostToNetwork32(kStunMagicCookie));
599 ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
600 ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
601 ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
602 return rtc::IPAddress(v6addr);
603 }
604 break;
605 }
606 }
607 }
608 // Invalid ip family or transaction ID, or missing owner.
609 // Return an AF_UNSPEC address.
610 return rtc::IPAddress();
611}
612
jbauchf1f87202016-03-30 06:43:37 -0700613bool StunXorAddressAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000614 if (!StunAddressAttribute::Read(buf))
615 return false;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200616 uint16_t xoredport = port() ^ (kStunMagicCookie >> 16);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000617 rtc::IPAddress xored_ip = GetXoredIP();
618 SetAddress(rtc::SocketAddress(xored_ip, xoredport));
619 return true;
620}
621
jbauchf1f87202016-03-30 06:43:37 -0700622bool StunXorAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000623 StunAddressFamily address_family = family();
624 if (address_family == STUN_ADDRESS_UNDEF) {
625 LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
626 return false;
627 }
628 rtc::IPAddress xored_ip = GetXoredIP();
629 if (xored_ip.family() == AF_UNSPEC) {
630 return false;
631 }
632 buf->WriteUInt8(0);
633 buf->WriteUInt8(family());
634 buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
635 switch (xored_ip.family()) {
636 case AF_INET: {
637 in_addr v4addr = xored_ip.ipv4_address();
638 buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
639 break;
640 }
641 case AF_INET6: {
642 in6_addr v6addr = xored_ip.ipv6_address();
643 buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
644 break;
645 }
646 }
647 return true;
648}
649
Peter Boström0c4e06b2015-10-07 12:23:21 +0200650StunUInt32Attribute::StunUInt32Attribute(uint16_t type, uint32_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000651 : StunAttribute(type, SIZE), bits_(value) {
652}
653
Peter Boström0c4e06b2015-10-07 12:23:21 +0200654StunUInt32Attribute::StunUInt32Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000655 : StunAttribute(type, SIZE), bits_(0) {
656}
657
658bool StunUInt32Attribute::GetBit(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800659 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000660 return static_cast<bool>((bits_ >> index) & 0x1);
661}
662
663void StunUInt32Attribute::SetBit(size_t index, bool value) {
nisseede5da42017-01-12 05:15:36 -0800664 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000665 bits_ &= ~(1 << index);
666 bits_ |= value ? (1 << index) : 0;
667}
668
jbauchf1f87202016-03-30 06:43:37 -0700669bool StunUInt32Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000670 if (length() != SIZE || !buf->ReadUInt32(&bits_))
671 return false;
672 return true;
673}
674
jbauchf1f87202016-03-30 06:43:37 -0700675bool StunUInt32Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000676 buf->WriteUInt32(bits_);
677 return true;
678}
679
Peter Boström0c4e06b2015-10-07 12:23:21 +0200680StunUInt64Attribute::StunUInt64Attribute(uint16_t type, uint64_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000681 : StunAttribute(type, SIZE), bits_(value) {
682}
683
Peter Boström0c4e06b2015-10-07 12:23:21 +0200684StunUInt64Attribute::StunUInt64Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000685 : StunAttribute(type, SIZE), bits_(0) {
686}
687
jbauchf1f87202016-03-30 06:43:37 -0700688bool StunUInt64Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000689 if (length() != SIZE || !buf->ReadUInt64(&bits_))
690 return false;
691 return true;
692}
693
jbauchf1f87202016-03-30 06:43:37 -0700694bool StunUInt64Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000695 buf->WriteUInt64(bits_);
696 return true;
697}
698
Peter Boström0c4e06b2015-10-07 12:23:21 +0200699StunByteStringAttribute::StunByteStringAttribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000700 : StunAttribute(type, 0), bytes_(NULL) {
701}
702
Peter Boström0c4e06b2015-10-07 12:23:21 +0200703StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000704 const std::string& str)
705 : StunAttribute(type, 0), bytes_(NULL) {
706 CopyBytes(str.c_str(), str.size());
707}
708
Peter Boström0c4e06b2015-10-07 12:23:21 +0200709StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000710 const void* bytes,
711 size_t length)
712 : StunAttribute(type, 0), bytes_(NULL) {
713 CopyBytes(bytes, length);
714}
715
Peter Boström0c4e06b2015-10-07 12:23:21 +0200716StunByteStringAttribute::StunByteStringAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000717 : StunAttribute(type, length), bytes_(NULL) {
718}
719
720StunByteStringAttribute::~StunByteStringAttribute() {
721 delete [] bytes_;
722}
723
724void StunByteStringAttribute::CopyBytes(const char* bytes) {
725 CopyBytes(bytes, strlen(bytes));
726}
727
728void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
729 char* new_bytes = new char[length];
730 memcpy(new_bytes, bytes, length);
731 SetBytes(new_bytes, length);
732}
733
Peter Boström0c4e06b2015-10-07 12:23:21 +0200734uint8_t StunByteStringAttribute::GetByte(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800735 RTC_DCHECK(bytes_ != NULL);
736 RTC_DCHECK(index < length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200737 return static_cast<uint8_t>(bytes_[index]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000738}
739
Peter Boström0c4e06b2015-10-07 12:23:21 +0200740void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
nisseede5da42017-01-12 05:15:36 -0800741 RTC_DCHECK(bytes_ != NULL);
742 RTC_DCHECK(index < length());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000743 bytes_[index] = value;
744}
745
jbauchf1f87202016-03-30 06:43:37 -0700746bool StunByteStringAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000747 bytes_ = new char[length()];
748 if (!buf->ReadBytes(bytes_, length())) {
749 return false;
750 }
751
752 ConsumePadding(buf);
753 return true;
754}
755
jbauchf1f87202016-03-30 06:43:37 -0700756bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000757 buf->WriteBytes(bytes_, length());
758 WritePadding(buf);
759 return true;
760}
761
762void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
763 delete [] bytes_;
764 bytes_ = bytes;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200765 SetLength(static_cast<uint16_t>(length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000766}
767
Peter Boström0c4e06b2015-10-07 12:23:21 +0200768StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type,
769 int code,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000770 const std::string& reason)
771 : StunAttribute(type, 0) {
772 SetCode(code);
773 SetReason(reason);
774}
775
Peter Boström0c4e06b2015-10-07 12:23:21 +0200776StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000777 : StunAttribute(type, length), class_(0), number_(0) {
778}
779
780StunErrorCodeAttribute::~StunErrorCodeAttribute() {
781}
782
783int StunErrorCodeAttribute::code() const {
784 return class_ * 100 + number_;
785}
786
787void StunErrorCodeAttribute::SetCode(int code) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200788 class_ = static_cast<uint8_t>(code / 100);
789 number_ = static_cast<uint8_t>(code % 100);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000790}
791
792void StunErrorCodeAttribute::SetReason(const std::string& reason) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200793 SetLength(MIN_SIZE + static_cast<uint16_t>(reason.size()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000794 reason_ = reason;
795}
796
jbauchf1f87202016-03-30 06:43:37 -0700797bool StunErrorCodeAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200798 uint32_t val;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000799 if (length() < MIN_SIZE || !buf->ReadUInt32(&val))
800 return false;
801
802 if ((val >> 11) != 0)
803 LOG(LS_ERROR) << "error-code bits not zero";
804
805 class_ = ((val >> 8) & 0x7);
806 number_ = (val & 0xff);
807
808 if (!buf->ReadString(&reason_, length() - 4))
809 return false;
810
811 ConsumePadding(buf);
812 return true;
813}
814
jbauchf1f87202016-03-30 06:43:37 -0700815bool StunErrorCodeAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000816 buf->WriteUInt32(class_ << 8 | number_);
817 buf->WriteString(reason_);
818 WritePadding(buf);
819 return true;
820}
821
Peter Boström0c4e06b2015-10-07 12:23:21 +0200822StunUInt16ListAttribute::StunUInt16ListAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000823 : StunAttribute(type, length) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200824 attr_types_ = new std::vector<uint16_t>();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000825}
826
827StunUInt16ListAttribute::~StunUInt16ListAttribute() {
828 delete attr_types_;
829}
830
831size_t StunUInt16ListAttribute::Size() const {
832 return attr_types_->size();
833}
834
Peter Boström0c4e06b2015-10-07 12:23:21 +0200835uint16_t StunUInt16ListAttribute::GetType(int index) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000836 return (*attr_types_)[index];
837}
838
Peter Boström0c4e06b2015-10-07 12:23:21 +0200839void StunUInt16ListAttribute::SetType(int index, uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000840 (*attr_types_)[index] = value;
841}
842
Peter Boström0c4e06b2015-10-07 12:23:21 +0200843void StunUInt16ListAttribute::AddType(uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000844 attr_types_->push_back(value);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200845 SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000846}
847
jbauchf1f87202016-03-30 06:43:37 -0700848bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000849 if (length() % 2)
850 return false;
851
852 for (size_t i = 0; i < length() / 2; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200853 uint16_t attr;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000854 if (!buf->ReadUInt16(&attr))
855 return false;
856 attr_types_->push_back(attr);
857 }
858 // Padding of these attributes is done in RFC 5389 style. This is
859 // slightly different from RFC3489, but it shouldn't be important.
860 // RFC3489 pads out to a 32 bit boundary by duplicating one of the
861 // entries in the list (not necessarily the last one - it's unspecified).
862 // RFC5389 pads on the end, and the bytes are always ignored.
863 ConsumePadding(buf);
864 return true;
865}
866
jbauchf1f87202016-03-30 06:43:37 -0700867bool StunUInt16ListAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000868 for (size_t i = 0; i < attr_types_->size(); ++i) {
869 buf->WriteUInt16((*attr_types_)[i]);
870 }
871 WritePadding(buf);
872 return true;
873}
874
875int GetStunSuccessResponseType(int req_type) {
876 return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
877}
878
879int GetStunErrorResponseType(int req_type) {
880 return IsStunRequestType(req_type) ? (req_type | 0x110) : -1;
881}
882
883bool IsStunRequestType(int msg_type) {
884 return ((msg_type & kStunTypeMask) == 0x000);
885}
886
887bool IsStunIndicationType(int msg_type) {
888 return ((msg_type & kStunTypeMask) == 0x010);
889}
890
891bool IsStunSuccessResponseType(int msg_type) {
892 return ((msg_type & kStunTypeMask) == 0x100);
893}
894
895bool IsStunErrorResponseType(int msg_type) {
896 return ((msg_type & kStunTypeMask) == 0x110);
897}
898
899bool ComputeStunCredentialHash(const std::string& username,
900 const std::string& realm,
901 const std::string& password,
902 std::string* hash) {
903 // http://tools.ietf.org/html/rfc5389#section-15.4
904 // long-term credentials will be calculated using the key and key is
905 // key = MD5(username ":" realm ":" SASLprep(password))
906 std::string input = username;
907 input += ':';
908 input += realm;
909 input += ':';
910 input += password;
911
912 char digest[rtc::MessageDigest::kMaxSize];
913 size_t size = rtc::ComputeDigest(
914 rtc::DIGEST_MD5, input.c_str(), input.size(),
915 digest, sizeof(digest));
916 if (size == 0) {
917 return false;
918 }
919
920 *hash = std::string(digest, size);
921 return true;
922}
923
924} // namespace cricket