1/*
2 * libjingle
3 * Copyright 2013 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/sctputils.h"
29
30#include "talk/base/buffer.h"
31#include "talk/base/bytebuffer.h"
32#include "talk/base/logging.h"
33
34namespace webrtc {
35
36// Format defined at
37// http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-01#section
38
39static const uint8 DATA_CHANNEL_OPEN_MESSAGE_TYPE = 0x03;
40static const uint8 DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE = 0x02;
41
42enum DataChannelOpenMessageChannelType {
43  DCOMCT_ORDERED_RELIABLE = 0x00,
44  DCOMCT_ORDERED_PARTIAL_RTXS = 0x01,
45  DCOMCT_ORDERED_PARTIAL_TIME = 0x02,
46  DCOMCT_UNORDERED_RELIABLE = 0x80,
47  DCOMCT_UNORDERED_PARTIAL_RTXS = 0x81,
48  DCOMCT_UNORDERED_PARTIAL_TIME = 0x82,
49};
50
51bool ParseDataChannelOpenMessage(const talk_base::Buffer& payload,
52                                 std::string* label,
53                                 DataChannelInit* config) {
54  // Format defined at
55  // http://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-04
56
57  talk_base::ByteBuffer buffer(payload.data(), payload.length());
58
59  uint8 message_type;
60  if (!buffer.ReadUInt8(&message_type)) {
61    LOG(LS_WARNING) << "Could not read OPEN message type.";
62    return false;
63  }
64  if (message_type != DATA_CHANNEL_OPEN_MESSAGE_TYPE) {
65    LOG(LS_WARNING) << "Data Channel OPEN message of unexpected type: "
66                    << message_type;
67    return false;
68  }
69
70  uint8 channel_type;
71  if (!buffer.ReadUInt8(&channel_type)) {
72    LOG(LS_WARNING) << "Could not read OPEN message channel type.";
73    return false;
74  }
75
76  uint16 priority;
77  if (!buffer.ReadUInt16(&priority)) {
78    LOG(LS_WARNING) << "Could not read OPEN message reliabilility prioirty.";
79    return false;
80  }
81  uint32 reliability_param;
82  if (!buffer.ReadUInt32(&reliability_param)) {
83    LOG(LS_WARNING) << "Could not read OPEN message reliabilility param.";
84    return false;
85  }
86  uint16 label_length;
87  if (!buffer.ReadUInt16(&label_length)) {
88    LOG(LS_WARNING) << "Could not read OPEN message label length.";
89    return false;
90  }
91  uint16 protocol_length;
92  if (!buffer.ReadUInt16(&protocol_length)) {
93    LOG(LS_WARNING) << "Could not read OPEN message protocol length.";
94    return false;
95  }
96  if (!buffer.ReadString(label, (size_t) label_length)) {
97    LOG(LS_WARNING) << "Could not read OPEN message label";
98    return false;
99  }
100  if (!buffer.ReadString(&config->protocol, protocol_length)) {
101    LOG(LS_WARNING) << "Could not read OPEN message protocol.";
102    return false;
103  }
104
105  config->ordered = true;
106  switch (channel_type) {
107    case DCOMCT_UNORDERED_RELIABLE:
108    case DCOMCT_UNORDERED_PARTIAL_RTXS:
109    case DCOMCT_UNORDERED_PARTIAL_TIME:
110      config->ordered = false;
111  }
112
113  config->maxRetransmits = -1;
114  config->maxRetransmitTime = -1;
115  switch (channel_type) {
116    case DCOMCT_ORDERED_PARTIAL_RTXS:
117    case DCOMCT_UNORDERED_PARTIAL_RTXS:
118      config->maxRetransmits = reliability_param;
119      break;
120    case DCOMCT_ORDERED_PARTIAL_TIME:
121    case DCOMCT_UNORDERED_PARTIAL_TIME:
122      config->maxRetransmitTime = reliability_param;
123      break;
124  }
125  return true;
126}
127
128bool ParseDataChannelOpenAckMessage(const talk_base::Buffer& payload) {
129  talk_base::ByteBuffer buffer(payload.data(), payload.length());
130
131  uint8 message_type;
132  if (!buffer.ReadUInt8(&message_type)) {
133    LOG(LS_WARNING) << "Could not read OPEN_ACK message type.";
134    return false;
135  }
136  if (message_type != DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE) {
137    LOG(LS_WARNING) << "Data Channel OPEN_ACK message of unexpected type: "
138                    << message_type;
139    return false;
140  }
141  return true;
142}
143
144bool WriteDataChannelOpenMessage(const std::string& label,
145                                 const DataChannelInit& config,
146                                 talk_base::Buffer* payload) {
147  // Format defined at
148  // http://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-00#section-6.1
149  uint8 channel_type = 0;
150  uint32 reliability_param = 0;
151  uint16 priority = 0;
152  if (config.ordered) {
153    if (config.maxRetransmits > -1) {
154      channel_type = DCOMCT_ORDERED_PARTIAL_RTXS;
155      reliability_param = config.maxRetransmits;
156    } else if (config.maxRetransmitTime > -1) {
157      channel_type = DCOMCT_ORDERED_PARTIAL_TIME;
158      reliability_param = config.maxRetransmitTime;
159    } else {
160      channel_type = DCOMCT_ORDERED_RELIABLE;
161    }
162  } else {
163    if (config.maxRetransmits > -1) {
164      channel_type = DCOMCT_UNORDERED_PARTIAL_RTXS;
165      reliability_param = config.maxRetransmits;
166    } else if (config.maxRetransmitTime > -1) {
167      channel_type = DCOMCT_UNORDERED_PARTIAL_TIME;
168      reliability_param = config.maxRetransmitTime;
169    } else {
170      channel_type = DCOMCT_UNORDERED_RELIABLE;
171    }
172  }
173
174  talk_base::ByteBuffer buffer(
175      NULL, 20 + label.length() + config.protocol.length(),
176      talk_base::ByteBuffer::ORDER_NETWORK);
177  buffer.WriteUInt8(DATA_CHANNEL_OPEN_MESSAGE_TYPE);
178  buffer.WriteUInt8(channel_type);
179  buffer.WriteUInt16(priority);
180  buffer.WriteUInt32(reliability_param);
181  buffer.WriteUInt16(static_cast<uint16>(label.length()));
182  buffer.WriteUInt16(static_cast<uint16>(config.protocol.length()));
183  buffer.WriteString(label);
184  buffer.WriteString(config.protocol);
185  payload->SetData(buffer.Data(), buffer.Length());
186  return true;
187}
188
189void WriteDataChannelOpenAckMessage(talk_base::Buffer* payload) {
190  talk_base::ByteBuffer buffer(talk_base::ByteBuffer::ORDER_NETWORK);
191  buffer.WriteUInt8(DATA_CHANNEL_OPEN_ACK_MESSAGE_TYPE);
192  payload->SetData(buffer.Data(), buffer.Length());
193}
194}  // namespace webrtc
195