blob: b66ac0308a326418b580a5c0e92a3fddf7d2cec6 [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 attrs_ = new std::vector<StunAttribute*>();
53}
54
55StunMessage::~StunMessage() {
56 for (size_t i = 0; i < attrs_->size(); i++)
57 delete (*attrs_)[i];
58 delete attrs_;
59}
60
61bool StunMessage::IsLegacy() const {
62 if (transaction_id_.size() == kStunLegacyTransactionIdLength)
63 return true;
nisseede5da42017-01-12 05:15:36 -080064 RTC_DCHECK(transaction_id_.size() == kStunTransactionIdLength);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000065 return false;
66}
67
68bool StunMessage::SetTransactionID(const std::string& str) {
69 if (!IsValidTransactionId(str)) {
70 return false;
71 }
72 transaction_id_ = str;
73 return true;
74}
75
nissecc99bc22017-02-02 01:31:30 -080076void StunMessage::AddAttribute(StunAttribute* attr) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000077 // Fail any attributes that aren't valid for this type of message.
nissecc99bc22017-02-02 01:31:30 -080078 RTC_DCHECK_EQ(attr->value_type(), GetAttributeValueType(attr->type()));
79
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000080 attrs_->push_back(attr);
81 attr->SetOwner(this);
82 size_t attr_length = attr->length();
83 if (attr_length % 4 != 0) {
84 attr_length += (4 - (attr_length % 4));
85 }
Peter Boström0c4e06b2015-10-07 12:23:21 +020086 length_ += static_cast<uint16_t>(attr_length + 4);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000087}
88
89const StunAddressAttribute* StunMessage::GetAddress(int type) const {
90 switch (type) {
91 case STUN_ATTR_MAPPED_ADDRESS: {
92 // Return XOR-MAPPED-ADDRESS when MAPPED-ADDRESS attribute is
93 // missing.
94 const StunAttribute* mapped_address =
95 GetAttribute(STUN_ATTR_MAPPED_ADDRESS);
96 if (!mapped_address)
97 mapped_address = GetAttribute(STUN_ATTR_XOR_MAPPED_ADDRESS);
98 return reinterpret_cast<const StunAddressAttribute*>(mapped_address);
99 }
100
101 default:
102 return static_cast<const StunAddressAttribute*>(GetAttribute(type));
103 }
104}
105
106const StunUInt32Attribute* StunMessage::GetUInt32(int type) const {
107 return static_cast<const StunUInt32Attribute*>(GetAttribute(type));
108}
109
110const StunUInt64Attribute* StunMessage::GetUInt64(int type) const {
111 return static_cast<const StunUInt64Attribute*>(GetAttribute(type));
112}
113
114const StunByteStringAttribute* StunMessage::GetByteString(int type) const {
115 return static_cast<const StunByteStringAttribute*>(GetAttribute(type));
116}
117
118const StunErrorCodeAttribute* StunMessage::GetErrorCode() const {
119 return static_cast<const StunErrorCodeAttribute*>(
120 GetAttribute(STUN_ATTR_ERROR_CODE));
121}
122
123const StunUInt16ListAttribute* StunMessage::GetUnknownAttributes() const {
124 return static_cast<const StunUInt16ListAttribute*>(
125 GetAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES));
126}
127
128// Verifies a STUN message has a valid MESSAGE-INTEGRITY attribute, using the
129// procedure outlined in RFC 5389, section 15.4.
130bool StunMessage::ValidateMessageIntegrity(const char* data, size_t size,
131 const std::string& password) {
132 // Verifying the size of the message.
katrielce4bda242016-06-09 08:45:45 -0700133 if ((size % 4) != 0 || size < kStunHeaderSize) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000134 return false;
135 }
136
137 // Getting the message length from the STUN header.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200138 uint16_t msg_length = rtc::GetBE16(&data[2]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000139 if (size != (msg_length + kStunHeaderSize)) {
140 return false;
141 }
142
143 // Finding Message Integrity attribute in stun message.
144 size_t current_pos = kStunHeaderSize;
145 bool has_message_integrity_attr = false;
katrielc1a206102016-06-20 05:13:16 -0700146 while (current_pos + 4 <= size) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200147 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000148 // Getting attribute type and length.
149 attr_type = rtc::GetBE16(&data[current_pos]);
150 attr_length = rtc::GetBE16(&data[current_pos + sizeof(attr_type)]);
151
152 // If M-I, sanity check it, and break out.
153 if (attr_type == STUN_ATTR_MESSAGE_INTEGRITY) {
154 if (attr_length != kStunMessageIntegritySize ||
katrielc1a206102016-06-20 05:13:16 -0700155 current_pos + sizeof(attr_type) + sizeof(attr_length) + attr_length >
156 size) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000157 return false;
158 }
159 has_message_integrity_attr = true;
160 break;
161 }
162
163 // Otherwise, skip to the next attribute.
164 current_pos += sizeof(attr_type) + sizeof(attr_length) + attr_length;
165 if ((attr_length % 4) != 0) {
166 current_pos += (4 - (attr_length % 4));
167 }
168 }
169
170 if (!has_message_integrity_attr) {
171 return false;
172 }
173
174 // Getting length of the message to calculate Message Integrity.
175 size_t mi_pos = current_pos;
kwiberg3ec46792016-04-27 07:22:53 -0700176 std::unique_ptr<char[]> temp_data(new char[current_pos]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000177 memcpy(temp_data.get(), data, current_pos);
178 if (size > mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize) {
179 // Stun message has other attributes after message integrity.
180 // Adjust the length parameter in stun message to calculate HMAC.
181 size_t extra_offset = size -
182 (mi_pos + kStunAttributeHeaderSize + kStunMessageIntegritySize);
183 size_t new_adjusted_len = size - extra_offset - kStunHeaderSize;
184
185 // Writing new length of the STUN message @ Message Length in temp buffer.
186 // 0 1 2 3
187 // 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
188 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
189 // |0 0| STUN Message Type | Message Length |
190 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Peter Boström0c4e06b2015-10-07 12:23:21 +0200191 rtc::SetBE16(temp_data.get() + 2, static_cast<uint16_t>(new_adjusted_len));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000192 }
193
194 char hmac[kStunMessageIntegritySize];
195 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
196 password.c_str(), password.size(),
197 temp_data.get(), mi_pos,
198 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800199 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000200 if (ret != sizeof(hmac))
201 return false;
202
203 // Comparing the calculated HMAC with the one present in the message.
204 return memcmp(data + current_pos + kStunAttributeHeaderSize,
205 hmac,
206 sizeof(hmac)) == 0;
207}
208
209bool StunMessage::AddMessageIntegrity(const std::string& password) {
210 return AddMessageIntegrity(password.c_str(), password.size());
211}
212
213bool StunMessage::AddMessageIntegrity(const char* key,
214 size_t keylen) {
215 // Add the attribute with a dummy value. Since this is a known attribute, it
216 // can't fail.
217 StunByteStringAttribute* msg_integrity_attr =
218 new StunByteStringAttribute(STUN_ATTR_MESSAGE_INTEGRITY,
219 std::string(kStunMessageIntegritySize, '0'));
nissecc99bc22017-02-02 01:31:30 -0800220 AddAttribute(msg_integrity_attr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000221
222 // Calculate the HMAC for the message.
jbauchf1f87202016-03-30 06:43:37 -0700223 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000224 if (!Write(&buf))
225 return false;
226
227 int msg_len_for_hmac = static_cast<int>(
228 buf.Length() - kStunAttributeHeaderSize - msg_integrity_attr->length());
229 char hmac[kStunMessageIntegritySize];
230 size_t ret = rtc::ComputeHmac(rtc::DIGEST_SHA_1,
231 key, keylen,
232 buf.Data(), msg_len_for_hmac,
233 hmac, sizeof(hmac));
nisseede5da42017-01-12 05:15:36 -0800234 RTC_DCHECK(ret == sizeof(hmac));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000235 if (ret != sizeof(hmac)) {
236 LOG(LS_ERROR) << "HMAC computation failed. Message-Integrity "
237 << "has dummy value.";
238 return false;
239 }
240
241 // Insert correct HMAC into the attribute.
242 msg_integrity_attr->CopyBytes(hmac, sizeof(hmac));
243 return true;
244}
245
246// Verifies a message is in fact a STUN message, by performing the checks
247// outlined in RFC 5389, section 7.3, including the FINGERPRINT check detailed
248// in section 15.5.
249bool StunMessage::ValidateFingerprint(const char* data, size_t size) {
250 // Check the message length.
251 size_t fingerprint_attr_size =
252 kStunAttributeHeaderSize + StunUInt32Attribute::SIZE;
253 if (size % 4 != 0 || size < kStunHeaderSize + fingerprint_attr_size)
254 return false;
255
256 // Skip the rest if the magic cookie isn't present.
257 const char* magic_cookie =
258 data + kStunTransactionIdOffset - kStunMagicCookieLength;
259 if (rtc::GetBE32(magic_cookie) != kStunMagicCookie)
260 return false;
261
262 // Check the fingerprint type and length.
263 const char* fingerprint_attr_data = data + size - fingerprint_attr_size;
264 if (rtc::GetBE16(fingerprint_attr_data) != STUN_ATTR_FINGERPRINT ||
Peter Boström0c4e06b2015-10-07 12:23:21 +0200265 rtc::GetBE16(fingerprint_attr_data + sizeof(uint16_t)) !=
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000266 StunUInt32Attribute::SIZE)
267 return false;
268
269 // Check the fingerprint value.
Peter Boström0c4e06b2015-10-07 12:23:21 +0200270 uint32_t fingerprint =
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000271 rtc::GetBE32(fingerprint_attr_data + kStunAttributeHeaderSize);
272 return ((fingerprint ^ STUN_FINGERPRINT_XOR_VALUE) ==
273 rtc::ComputeCrc32(data, size - fingerprint_attr_size));
274}
275
276bool StunMessage::AddFingerprint() {
277 // Add the attribute with a dummy value. Since this is a known attribute,
278 // it can't fail.
279 StunUInt32Attribute* fingerprint_attr =
280 new StunUInt32Attribute(STUN_ATTR_FINGERPRINT, 0);
nissecc99bc22017-02-02 01:31:30 -0800281 AddAttribute(fingerprint_attr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000282
283 // Calculate the CRC-32 for the message and insert it.
jbauchf1f87202016-03-30 06:43:37 -0700284 ByteBufferWriter buf;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000285 if (!Write(&buf))
286 return false;
287
288 int msg_len_for_crc32 = static_cast<int>(
289 buf.Length() - kStunAttributeHeaderSize - fingerprint_attr->length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200290 uint32_t c = rtc::ComputeCrc32(buf.Data(), msg_len_for_crc32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000291
292 // Insert the correct CRC-32, XORed with a constant, into the attribute.
293 fingerprint_attr->SetValue(c ^ STUN_FINGERPRINT_XOR_VALUE);
294 return true;
295}
296
jbauchf1f87202016-03-30 06:43:37 -0700297bool StunMessage::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000298 if (!buf->ReadUInt16(&type_))
299 return false;
300
301 if (type_ & 0x8000) {
302 // RTP and RTCP set the MSB of first byte, since first two bits are version,
303 // and version is always 2 (10). If set, this is not a STUN packet.
304 return false;
305 }
306
307 if (!buf->ReadUInt16(&length_))
308 return false;
309
310 std::string magic_cookie;
311 if (!buf->ReadString(&magic_cookie, kStunMagicCookieLength))
312 return false;
313
314 std::string transaction_id;
315 if (!buf->ReadString(&transaction_id, kStunTransactionIdLength))
316 return false;
317
Peter Boström0c4e06b2015-10-07 12:23:21 +0200318 uint32_t magic_cookie_int =
319 *reinterpret_cast<const uint32_t*>(magic_cookie.data());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000320 if (rtc::NetworkToHost32(magic_cookie_int) != kStunMagicCookie) {
321 // If magic cookie is invalid it means that the peer implements
322 // RFC3489 instead of RFC5389.
323 transaction_id.insert(0, magic_cookie);
324 }
nisseede5da42017-01-12 05:15:36 -0800325 RTC_DCHECK(IsValidTransactionId(transaction_id));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000326 transaction_id_ = transaction_id;
327
328 if (length_ != buf->Length())
329 return false;
330
331 attrs_->resize(0);
332
333 size_t rest = buf->Length() - length_;
334 while (buf->Length() > rest) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200335 uint16_t attr_type, attr_length;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000336 if (!buf->ReadUInt16(&attr_type))
337 return false;
338 if (!buf->ReadUInt16(&attr_length))
339 return false;
340
Honghai Zhang3e024302016-09-22 09:52:16 -0700341 std::unique_ptr<StunAttribute> attr(
342 CreateAttribute(attr_type, attr_length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000343 if (!attr) {
344 // Skip any unknown or malformed attributes.
345 if ((attr_length % 4) != 0) {
346 attr_length += (4 - (attr_length % 4));
347 }
348 if (!buf->Consume(attr_length))
349 return false;
350 } else {
351 if (!attr->Read(buf))
352 return false;
Honghai Zhang3e024302016-09-22 09:52:16 -0700353 // TODO(honghaiz): Change |attrs_| to be a vector of unique_ptrs.
354 attrs_->push_back(attr.release());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000355 }
356 }
357
nisseede5da42017-01-12 05:15:36 -0800358 RTC_DCHECK(buf->Length() == rest);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000359 return true;
360}
361
jbauchf1f87202016-03-30 06:43:37 -0700362bool StunMessage::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000363 buf->WriteUInt16(type_);
364 buf->WriteUInt16(length_);
365 if (!IsLegacy())
366 buf->WriteUInt32(kStunMagicCookie);
367 buf->WriteString(transaction_id_);
368
369 for (size_t i = 0; i < attrs_->size(); ++i) {
370 buf->WriteUInt16((*attrs_)[i]->type());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200371 buf->WriteUInt16(static_cast<uint16_t>((*attrs_)[i]->length()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000372 if (!(*attrs_)[i]->Write(buf))
373 return false;
374 }
375
376 return true;
377}
378
379StunAttributeValueType StunMessage::GetAttributeValueType(int type) const {
380 switch (type) {
381 case STUN_ATTR_MAPPED_ADDRESS: return STUN_VALUE_ADDRESS;
382 case STUN_ATTR_USERNAME: return STUN_VALUE_BYTE_STRING;
383 case STUN_ATTR_MESSAGE_INTEGRITY: return STUN_VALUE_BYTE_STRING;
384 case STUN_ATTR_ERROR_CODE: return STUN_VALUE_ERROR_CODE;
385 case STUN_ATTR_UNKNOWN_ATTRIBUTES: return STUN_VALUE_UINT16_LIST;
386 case STUN_ATTR_REALM: return STUN_VALUE_BYTE_STRING;
387 case STUN_ATTR_NONCE: return STUN_VALUE_BYTE_STRING;
388 case STUN_ATTR_XOR_MAPPED_ADDRESS: return STUN_VALUE_XOR_ADDRESS;
389 case STUN_ATTR_SOFTWARE: return STUN_VALUE_BYTE_STRING;
390 case STUN_ATTR_ALTERNATE_SERVER: return STUN_VALUE_ADDRESS;
391 case STUN_ATTR_FINGERPRINT: return STUN_VALUE_UINT32;
pthatcher@webrtc.org0ba15332015-01-10 00:47:02 +0000392 case STUN_ATTR_ORIGIN: return STUN_VALUE_BYTE_STRING;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000393 case STUN_ATTR_RETRANSMIT_COUNT: return STUN_VALUE_UINT32;
394 default: return STUN_VALUE_UNKNOWN;
395 }
396}
397
398StunAttribute* StunMessage::CreateAttribute(int type, size_t length) /*const*/ {
399 StunAttributeValueType value_type = GetAttributeValueType(type);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200400 return StunAttribute::Create(value_type, type, static_cast<uint16_t>(length),
401 this);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000402}
403
404const StunAttribute* StunMessage::GetAttribute(int type) const {
405 for (size_t i = 0; i < attrs_->size(); ++i) {
406 if ((*attrs_)[i]->type() == type)
407 return (*attrs_)[i];
408 }
409 return NULL;
410}
411
412bool StunMessage::IsValidTransactionId(const std::string& transaction_id) {
413 return transaction_id.size() == kStunTransactionIdLength ||
414 transaction_id.size() == kStunLegacyTransactionIdLength;
415}
416
417// StunAttribute
418
Peter Boström0c4e06b2015-10-07 12:23:21 +0200419StunAttribute::StunAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000420 : type_(type), length_(length) {
421}
422
jbauchf1f87202016-03-30 06:43:37 -0700423void StunAttribute::ConsumePadding(ByteBufferReader* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000424 int remainder = length_ % 4;
425 if (remainder > 0) {
426 buf->Consume(4 - remainder);
427 }
428}
429
jbauchf1f87202016-03-30 06:43:37 -0700430void StunAttribute::WritePadding(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000431 int remainder = length_ % 4;
432 if (remainder > 0) {
433 char zeroes[4] = {0};
434 buf->WriteBytes(zeroes, 4 - remainder);
435 }
436}
437
438StunAttribute* StunAttribute::Create(StunAttributeValueType value_type,
Peter Boström0c4e06b2015-10-07 12:23:21 +0200439 uint16_t type,
440 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000441 StunMessage* owner) {
442 switch (value_type) {
443 case STUN_VALUE_ADDRESS:
444 return new StunAddressAttribute(type, length);
445 case STUN_VALUE_XOR_ADDRESS:
446 return new StunXorAddressAttribute(type, length, owner);
447 case STUN_VALUE_UINT32:
448 return new StunUInt32Attribute(type);
449 case STUN_VALUE_UINT64:
450 return new StunUInt64Attribute(type);
451 case STUN_VALUE_BYTE_STRING:
452 return new StunByteStringAttribute(type, length);
453 case STUN_VALUE_ERROR_CODE:
454 return new StunErrorCodeAttribute(type, length);
455 case STUN_VALUE_UINT16_LIST:
456 return new StunUInt16ListAttribute(type, length);
457 default:
458 return NULL;
459 }
460}
461
Peter Boström0c4e06b2015-10-07 12:23:21 +0200462StunAddressAttribute* StunAttribute::CreateAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000463 return new StunAddressAttribute(type, 0);
464}
465
Peter Boström0c4e06b2015-10-07 12:23:21 +0200466StunXorAddressAttribute* StunAttribute::CreateXorAddress(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000467 return new StunXorAddressAttribute(type, 0, NULL);
468}
469
Peter Boström0c4e06b2015-10-07 12:23:21 +0200470StunUInt64Attribute* StunAttribute::CreateUInt64(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000471 return new StunUInt64Attribute(type);
472}
473
Peter Boström0c4e06b2015-10-07 12:23:21 +0200474StunUInt32Attribute* StunAttribute::CreateUInt32(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000475 return new StunUInt32Attribute(type);
476}
477
Peter Boström0c4e06b2015-10-07 12:23:21 +0200478StunByteStringAttribute* StunAttribute::CreateByteString(uint16_t type) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000479 return new StunByteStringAttribute(type, 0);
480}
481
482StunErrorCodeAttribute* StunAttribute::CreateErrorCode() {
483 return new StunErrorCodeAttribute(
484 STUN_ATTR_ERROR_CODE, StunErrorCodeAttribute::MIN_SIZE);
485}
486
487StunUInt16ListAttribute* StunAttribute::CreateUnknownAttributes() {
488 return new StunUInt16ListAttribute(STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
489}
490
Peter Boström0c4e06b2015-10-07 12:23:21 +0200491StunAddressAttribute::StunAddressAttribute(uint16_t type,
492 const rtc::SocketAddress& addr)
493 : StunAttribute(type, 0) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000494 SetAddress(addr);
495}
496
Peter Boström0c4e06b2015-10-07 12:23:21 +0200497StunAddressAttribute::StunAddressAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000498 : StunAttribute(type, length) {
499}
500
jbauchf1f87202016-03-30 06:43:37 -0700501bool StunAddressAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200502 uint8_t dummy;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000503 if (!buf->ReadUInt8(&dummy))
504 return false;
505
Peter Boström0c4e06b2015-10-07 12:23:21 +0200506 uint8_t stun_family;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000507 if (!buf->ReadUInt8(&stun_family)) {
508 return false;
509 }
Peter Boström0c4e06b2015-10-07 12:23:21 +0200510 uint16_t port;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000511 if (!buf->ReadUInt16(&port))
512 return false;
513 if (stun_family == STUN_ADDRESS_IPV4) {
514 in_addr v4addr;
515 if (length() != SIZE_IP4) {
516 return false;
517 }
518 if (!buf->ReadBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr))) {
519 return false;
520 }
521 rtc::IPAddress ipaddr(v4addr);
522 SetAddress(rtc::SocketAddress(ipaddr, port));
523 } else if (stun_family == STUN_ADDRESS_IPV6) {
524 in6_addr v6addr;
525 if (length() != SIZE_IP6) {
526 return false;
527 }
528 if (!buf->ReadBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr))) {
529 return false;
530 }
531 rtc::IPAddress ipaddr(v6addr);
532 SetAddress(rtc::SocketAddress(ipaddr, port));
533 } else {
534 return false;
535 }
536 return true;
537}
538
jbauchf1f87202016-03-30 06:43:37 -0700539bool StunAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000540 StunAddressFamily address_family = family();
541 if (address_family == STUN_ADDRESS_UNDEF) {
542 LOG(LS_ERROR) << "Error writing address attribute: unknown family.";
543 return false;
544 }
545 buf->WriteUInt8(0);
546 buf->WriteUInt8(address_family);
547 buf->WriteUInt16(address_.port());
548 switch (address_.family()) {
549 case AF_INET: {
550 in_addr v4addr = address_.ipaddr().ipv4_address();
551 buf->WriteBytes(reinterpret_cast<char*>(&v4addr), sizeof(v4addr));
552 break;
553 }
554 case AF_INET6: {
555 in6_addr v6addr = address_.ipaddr().ipv6_address();
556 buf->WriteBytes(reinterpret_cast<char*>(&v6addr), sizeof(v6addr));
557 break;
558 }
559 }
560 return true;
561}
562
Peter Boström0c4e06b2015-10-07 12:23:21 +0200563StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
564 const rtc::SocketAddress& addr)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000565 : StunAddressAttribute(type, addr), owner_(NULL) {
566}
567
Peter Boström0c4e06b2015-10-07 12:23:21 +0200568StunXorAddressAttribute::StunXorAddressAttribute(uint16_t type,
569 uint16_t length,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000570 StunMessage* owner)
Peter Boström0c4e06b2015-10-07 12:23:21 +0200571 : StunAddressAttribute(type, length), owner_(owner) {
572}
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000573
574rtc::IPAddress StunXorAddressAttribute::GetXoredIP() const {
575 if (owner_) {
576 rtc::IPAddress ip = ipaddr();
577 switch (ip.family()) {
578 case AF_INET: {
579 in_addr v4addr = ip.ipv4_address();
580 v4addr.s_addr =
581 (v4addr.s_addr ^ rtc::HostToNetwork32(kStunMagicCookie));
582 return rtc::IPAddress(v4addr);
583 }
584 case AF_INET6: {
585 in6_addr v6addr = ip.ipv6_address();
586 const std::string& transaction_id = owner_->transaction_id();
587 if (transaction_id.length() == kStunTransactionIdLength) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200588 uint32_t transactionid_as_ints[3];
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000589 memcpy(&transactionid_as_ints[0], transaction_id.c_str(),
590 transaction_id.length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200591 uint32_t* ip_as_ints = reinterpret_cast<uint32_t*>(&v6addr.s6_addr);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000592 // Transaction ID is in network byte order, but magic cookie
593 // is stored in host byte order.
594 ip_as_ints[0] =
595 (ip_as_ints[0] ^ rtc::HostToNetwork32(kStunMagicCookie));
596 ip_as_ints[1] = (ip_as_ints[1] ^ transactionid_as_ints[0]);
597 ip_as_ints[2] = (ip_as_ints[2] ^ transactionid_as_ints[1]);
598 ip_as_ints[3] = (ip_as_ints[3] ^ transactionid_as_ints[2]);
599 return rtc::IPAddress(v6addr);
600 }
601 break;
602 }
603 }
604 }
605 // Invalid ip family or transaction ID, or missing owner.
606 // Return an AF_UNSPEC address.
607 return rtc::IPAddress();
608}
609
jbauchf1f87202016-03-30 06:43:37 -0700610bool StunXorAddressAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000611 if (!StunAddressAttribute::Read(buf))
612 return false;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200613 uint16_t xoredport = port() ^ (kStunMagicCookie >> 16);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000614 rtc::IPAddress xored_ip = GetXoredIP();
615 SetAddress(rtc::SocketAddress(xored_ip, xoredport));
616 return true;
617}
618
jbauchf1f87202016-03-30 06:43:37 -0700619bool StunXorAddressAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000620 StunAddressFamily address_family = family();
621 if (address_family == STUN_ADDRESS_UNDEF) {
622 LOG(LS_ERROR) << "Error writing xor-address attribute: unknown family.";
623 return false;
624 }
625 rtc::IPAddress xored_ip = GetXoredIP();
626 if (xored_ip.family() == AF_UNSPEC) {
627 return false;
628 }
629 buf->WriteUInt8(0);
630 buf->WriteUInt8(family());
631 buf->WriteUInt16(port() ^ (kStunMagicCookie >> 16));
632 switch (xored_ip.family()) {
633 case AF_INET: {
634 in_addr v4addr = xored_ip.ipv4_address();
635 buf->WriteBytes(reinterpret_cast<const char*>(&v4addr), sizeof(v4addr));
636 break;
637 }
638 case AF_INET6: {
639 in6_addr v6addr = xored_ip.ipv6_address();
640 buf->WriteBytes(reinterpret_cast<const char*>(&v6addr), sizeof(v6addr));
641 break;
642 }
643 }
644 return true;
645}
646
Peter Boström0c4e06b2015-10-07 12:23:21 +0200647StunUInt32Attribute::StunUInt32Attribute(uint16_t type, uint32_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000648 : StunAttribute(type, SIZE), bits_(value) {
649}
650
Peter Boström0c4e06b2015-10-07 12:23:21 +0200651StunUInt32Attribute::StunUInt32Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000652 : StunAttribute(type, SIZE), bits_(0) {
653}
654
655bool StunUInt32Attribute::GetBit(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800656 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000657 return static_cast<bool>((bits_ >> index) & 0x1);
658}
659
660void StunUInt32Attribute::SetBit(size_t index, bool value) {
nisseede5da42017-01-12 05:15:36 -0800661 RTC_DCHECK(index < 32);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000662 bits_ &= ~(1 << index);
663 bits_ |= value ? (1 << index) : 0;
664}
665
jbauchf1f87202016-03-30 06:43:37 -0700666bool StunUInt32Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000667 if (length() != SIZE || !buf->ReadUInt32(&bits_))
668 return false;
669 return true;
670}
671
jbauchf1f87202016-03-30 06:43:37 -0700672bool StunUInt32Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000673 buf->WriteUInt32(bits_);
674 return true;
675}
676
Peter Boström0c4e06b2015-10-07 12:23:21 +0200677StunUInt64Attribute::StunUInt64Attribute(uint16_t type, uint64_t value)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000678 : StunAttribute(type, SIZE), bits_(value) {
679}
680
Peter Boström0c4e06b2015-10-07 12:23:21 +0200681StunUInt64Attribute::StunUInt64Attribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000682 : StunAttribute(type, SIZE), bits_(0) {
683}
684
jbauchf1f87202016-03-30 06:43:37 -0700685bool StunUInt64Attribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000686 if (length() != SIZE || !buf->ReadUInt64(&bits_))
687 return false;
688 return true;
689}
690
jbauchf1f87202016-03-30 06:43:37 -0700691bool StunUInt64Attribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000692 buf->WriteUInt64(bits_);
693 return true;
694}
695
Peter Boström0c4e06b2015-10-07 12:23:21 +0200696StunByteStringAttribute::StunByteStringAttribute(uint16_t type)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000697 : StunAttribute(type, 0), bytes_(NULL) {
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 std::string& str)
702 : StunAttribute(type, 0), bytes_(NULL) {
703 CopyBytes(str.c_str(), str.size());
704}
705
Peter Boström0c4e06b2015-10-07 12:23:21 +0200706StunByteStringAttribute::StunByteStringAttribute(uint16_t type,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000707 const void* bytes,
708 size_t length)
709 : StunAttribute(type, 0), bytes_(NULL) {
710 CopyBytes(bytes, length);
711}
712
Peter Boström0c4e06b2015-10-07 12:23:21 +0200713StunByteStringAttribute::StunByteStringAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000714 : StunAttribute(type, length), bytes_(NULL) {
715}
716
717StunByteStringAttribute::~StunByteStringAttribute() {
718 delete [] bytes_;
719}
720
721void StunByteStringAttribute::CopyBytes(const char* bytes) {
722 CopyBytes(bytes, strlen(bytes));
723}
724
725void StunByteStringAttribute::CopyBytes(const void* bytes, size_t length) {
726 char* new_bytes = new char[length];
727 memcpy(new_bytes, bytes, length);
728 SetBytes(new_bytes, length);
729}
730
Peter Boström0c4e06b2015-10-07 12:23:21 +0200731uint8_t StunByteStringAttribute::GetByte(size_t index) const {
nisseede5da42017-01-12 05:15:36 -0800732 RTC_DCHECK(bytes_ != NULL);
733 RTC_DCHECK(index < length());
Peter Boström0c4e06b2015-10-07 12:23:21 +0200734 return static_cast<uint8_t>(bytes_[index]);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000735}
736
Peter Boström0c4e06b2015-10-07 12:23:21 +0200737void StunByteStringAttribute::SetByte(size_t index, uint8_t value) {
nisseede5da42017-01-12 05:15:36 -0800738 RTC_DCHECK(bytes_ != NULL);
739 RTC_DCHECK(index < length());
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000740 bytes_[index] = value;
741}
742
jbauchf1f87202016-03-30 06:43:37 -0700743bool StunByteStringAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000744 bytes_ = new char[length()];
745 if (!buf->ReadBytes(bytes_, length())) {
746 return false;
747 }
748
749 ConsumePadding(buf);
750 return true;
751}
752
jbauchf1f87202016-03-30 06:43:37 -0700753bool StunByteStringAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000754 buf->WriteBytes(bytes_, length());
755 WritePadding(buf);
756 return true;
757}
758
759void StunByteStringAttribute::SetBytes(char* bytes, size_t length) {
760 delete [] bytes_;
761 bytes_ = bytes;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200762 SetLength(static_cast<uint16_t>(length));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000763}
764
Peter Boström0c4e06b2015-10-07 12:23:21 +0200765StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type,
766 int code,
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000767 const std::string& reason)
768 : StunAttribute(type, 0) {
769 SetCode(code);
770 SetReason(reason);
771}
772
Peter Boström0c4e06b2015-10-07 12:23:21 +0200773StunErrorCodeAttribute::StunErrorCodeAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000774 : StunAttribute(type, length), class_(0), number_(0) {
775}
776
777StunErrorCodeAttribute::~StunErrorCodeAttribute() {
778}
779
780int StunErrorCodeAttribute::code() const {
781 return class_ * 100 + number_;
782}
783
784void StunErrorCodeAttribute::SetCode(int code) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200785 class_ = static_cast<uint8_t>(code / 100);
786 number_ = static_cast<uint8_t>(code % 100);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000787}
788
789void StunErrorCodeAttribute::SetReason(const std::string& reason) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200790 SetLength(MIN_SIZE + static_cast<uint16_t>(reason.size()));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000791 reason_ = reason;
792}
793
jbauchf1f87202016-03-30 06:43:37 -0700794bool StunErrorCodeAttribute::Read(ByteBufferReader* buf) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200795 uint32_t val;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000796 if (length() < MIN_SIZE || !buf->ReadUInt32(&val))
797 return false;
798
799 if ((val >> 11) != 0)
800 LOG(LS_ERROR) << "error-code bits not zero";
801
802 class_ = ((val >> 8) & 0x7);
803 number_ = (val & 0xff);
804
805 if (!buf->ReadString(&reason_, length() - 4))
806 return false;
807
808 ConsumePadding(buf);
809 return true;
810}
811
jbauchf1f87202016-03-30 06:43:37 -0700812bool StunErrorCodeAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000813 buf->WriteUInt32(class_ << 8 | number_);
814 buf->WriteString(reason_);
815 WritePadding(buf);
816 return true;
817}
818
Peter Boström0c4e06b2015-10-07 12:23:21 +0200819StunUInt16ListAttribute::StunUInt16ListAttribute(uint16_t type, uint16_t length)
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000820 : StunAttribute(type, length) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200821 attr_types_ = new std::vector<uint16_t>();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000822}
823
824StunUInt16ListAttribute::~StunUInt16ListAttribute() {
825 delete attr_types_;
826}
827
828size_t StunUInt16ListAttribute::Size() const {
829 return attr_types_->size();
830}
831
Peter Boström0c4e06b2015-10-07 12:23:21 +0200832uint16_t StunUInt16ListAttribute::GetType(int index) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000833 return (*attr_types_)[index];
834}
835
Peter Boström0c4e06b2015-10-07 12:23:21 +0200836void StunUInt16ListAttribute::SetType(int index, uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000837 (*attr_types_)[index] = value;
838}
839
Peter Boström0c4e06b2015-10-07 12:23:21 +0200840void StunUInt16ListAttribute::AddType(uint16_t value) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000841 attr_types_->push_back(value);
Peter Boström0c4e06b2015-10-07 12:23:21 +0200842 SetLength(static_cast<uint16_t>(attr_types_->size() * 2));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000843}
844
jbauchf1f87202016-03-30 06:43:37 -0700845bool StunUInt16ListAttribute::Read(ByteBufferReader* buf) {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000846 if (length() % 2)
847 return false;
848
849 for (size_t i = 0; i < length() / 2; i++) {
Peter Boström0c4e06b2015-10-07 12:23:21 +0200850 uint16_t attr;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000851 if (!buf->ReadUInt16(&attr))
852 return false;
853 attr_types_->push_back(attr);
854 }
855 // Padding of these attributes is done in RFC 5389 style. This is
856 // slightly different from RFC3489, but it shouldn't be important.
857 // RFC3489 pads out to a 32 bit boundary by duplicating one of the
858 // entries in the list (not necessarily the last one - it's unspecified).
859 // RFC5389 pads on the end, and the bytes are always ignored.
860 ConsumePadding(buf);
861 return true;
862}
863
jbauchf1f87202016-03-30 06:43:37 -0700864bool StunUInt16ListAttribute::Write(ByteBufferWriter* buf) const {
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000865 for (size_t i = 0; i < attr_types_->size(); ++i) {
866 buf->WriteUInt16((*attr_types_)[i]);
867 }
868 WritePadding(buf);
869 return true;
870}
871
872int GetStunSuccessResponseType(int req_type) {
873 return IsStunRequestType(req_type) ? (req_type | 0x100) : -1;
874}
875
876int GetStunErrorResponseType(int req_type) {
877 return IsStunRequestType(req_type) ? (req_type | 0x110) : -1;
878}
879
880bool IsStunRequestType(int msg_type) {
881 return ((msg_type & kStunTypeMask) == 0x000);
882}
883
884bool IsStunIndicationType(int msg_type) {
885 return ((msg_type & kStunTypeMask) == 0x010);
886}
887
888bool IsStunSuccessResponseType(int msg_type) {
889 return ((msg_type & kStunTypeMask) == 0x100);
890}
891
892bool IsStunErrorResponseType(int msg_type) {
893 return ((msg_type & kStunTypeMask) == 0x110);
894}
895
896bool ComputeStunCredentialHash(const std::string& username,
897 const std::string& realm,
898 const std::string& password,
899 std::string* hash) {
900 // http://tools.ietf.org/html/rfc5389#section-15.4
901 // long-term credentials will be calculated using the key and key is
902 // key = MD5(username ":" realm ":" SASLprep(password))
903 std::string input = username;
904 input += ':';
905 input += realm;
906 input += ':';
907 input += password;
908
909 char digest[rtc::MessageDigest::kMaxSize];
910 size_t size = rtc::ComputeDigest(
911 rtc::DIGEST_MD5, input.c_str(), input.size(),
912 digest, sizeof(digest));
913 if (size == 0) {
914 return false;
915 }
916
917 *hash = std::string(digest, size);
918 return true;
919}
920
921} // namespace cricket