stun.c 6.24 KB
Newer Older
dafydd.harries's avatar
import  
dafydd.harries committed
1
2
3
4
5
6

#include "stun.h"

#include <arpa/inet.h>
#include <string.h>

7
G_GNUC_WARN_UNUSED_RESULT
dafydd.harries's avatar
import  
dafydd.harries committed
8
static StunAttribute *
9
stun_attribute_new (guint type)
dafydd.harries's avatar
import  
dafydd.harries committed
10
{
11
  StunAttribute *attr = g_slice_new0 (StunAttribute);
dafydd.harries's avatar
import  
dafydd.harries committed
12
13
14
15
16
17

  attr->type = type;
  return attr;
}

StunAttribute *
18
stun_attribute_mapped_address_new (guint32 ip, guint16 port)
dafydd.harries's avatar
import  
dafydd.harries committed
19
{
20
  StunAttribute *attr = stun_attribute_new (STUN_ATTRIBUTE_MAPPED_ADDRESS);
dafydd.harries's avatar
import  
dafydd.harries committed
21
22
23
24
25
26
27
28
29
30

  attr->length = 8;
  attr->address.padding = 0;
  attr->address.af = 1;
  attr->address.ip = ip;
  attr->address.port = port;
  return attr;
}

void
31
stun_attribute_free (StunAttribute *attr)
dafydd.harries's avatar
import  
dafydd.harries committed
32
{
33
  g_slice_free (StunAttribute, attr);
dafydd.harries's avatar
import  
dafydd.harries committed
34
35
}

36
G_GNUC_WARN_UNUSED_RESULT
37
static gboolean
38
_stun_attribute_unpack (StunAttribute *attr, guint length, const gchar *s)
dafydd.harries's avatar
import  
dafydd.harries committed
39
{
40
  guint type;
dafydd.harries's avatar
import  
dafydd.harries committed
41

42
43
44
45
46
47
48
  if (length < 4)
    /* must start with 16 bit type, 16 bit length */
    return FALSE;

  type = ntohs (*(guint16 *) s);

  switch (type)
49
50
    {
      case STUN_ATTRIBUTE_MAPPED_ADDRESS:
51
52
53
        if (length != 12)
          return FALSE;

54
55
56
57
58
        attr->address.af = (guint8) s[5];
        g_assert (attr->address.af == 1);
        attr->address.port = ntohs (*(guint16 *)(s + 6));
        attr->address.ip = ntohl (*(guint32 *)(s + 8));
        break;
59

60
      default:
Dafydd Harries's avatar
Dafydd Harries committed
61
        /* unknown attribute; we can only unpack the type */
62
63
        break;
    }
64

65
  attr->type = type;
66
  return TRUE;
67
68
69
70
71
72
73
74
}

StunAttribute *
stun_attribute_unpack (guint length, const gchar *s)
{
  StunAttribute *attr;

  attr = stun_attribute_new (0);
dafydd.harries's avatar
import  
dafydd.harries committed
75

76
77
78
79
  if (_stun_attribute_unpack (attr, length, s))
    return attr;
  else
    return NULL;
dafydd.harries's avatar
import  
dafydd.harries committed
80
81
82
}

guint
83
stun_attribute_pack (StunAttribute *attr, gchar **packed)
dafydd.harries's avatar
import  
dafydd.harries committed
84
{
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  switch (attr->type)
    {
      case STUN_ATTRIBUTE_MAPPED_ADDRESS:
        {
          StunAttribute *ret = g_malloc0 (sizeof (StunAttribute));

          ret->type = htons (attr->type);
          ret->length = htons (8);
          ret->address.af = attr->address.af;
          ret->address.port = htons (attr->address.port);
          ret->address.ip = htonl (attr->address.ip);
          *packed = (gchar *) ret;
          return 12;
        }
      default:
        return 0;
dafydd.harries's avatar
import  
dafydd.harries committed
101
102
103
104
  }
}

gchar *
105
stun_attribute_dump (StunAttribute *attr)
dafydd.harries's avatar
import  
dafydd.harries committed
106
{
107
108
109
110
111
112
113
114
115
116
117
118
119
  switch (attr->type)
    {
      case STUN_ATTRIBUTE_MAPPED_ADDRESS:
        return g_strdup_printf (
          "MAPPED-ADDRESS %d.%d.%d.%d:%d",
            (attr->address.ip & 0xff000000) >> 24,
            (attr->address.ip & 0x00ff0000) >> 16,
            (attr->address.ip & 0x0000ff00) >>  8,
            (attr->address.ip & 0x000000ff) >>  0,
            attr->address.port);
      default:
        return g_strdup_printf ("UNKNOWN (%d)", attr->type);
    }
dafydd.harries's avatar
import  
dafydd.harries committed
120
121
}

122
123
124
125
126
127
void
stun_message_init (StunMessage *msg, guint type)
{
  msg->type = type;
}

128
StunMessage *
129
stun_message_new (guint type)
dafydd.harries's avatar
import  
dafydd.harries committed
130
{
131
  StunMessage *msg = g_slice_new0 (StunMessage);
dafydd.harries's avatar
import  
dafydd.harries committed
132

133
  stun_message_init (msg, type);
dafydd.harries's avatar
import  
dafydd.harries committed
134
135
136
137
  return msg;
}

StunMessage *
138
stun_message_binding_request_new ()
dafydd.harries's avatar
import  
dafydd.harries committed
139
{
140
  return stun_message_new (STUN_MESSAGE_BINDING_REQUEST);
dafydd.harries's avatar
import  
dafydd.harries committed
141
142
143
}

void
144
stun_message_free (StunMessage *msg)
dafydd.harries's avatar
import  
dafydd.harries committed
145
146
147
{
  StunAttribute **attr;

148
149
150
151
  if (msg->attributes)
    {
      for (attr = msg->attributes; *attr; attr++)
        stun_attribute_free (*attr);
dafydd.harries's avatar
import  
dafydd.harries committed
152

153
154
      g_free (msg->attributes);
    }
dafydd.harries's avatar
import  
dafydd.harries committed
155

156
  g_slice_free (StunMessage, msg);
dafydd.harries's avatar
import  
dafydd.harries committed
157
158
159
}

StunMessage *
160
stun_message_unpack (guint length, gchar *s)
dafydd.harries's avatar
import  
dafydd.harries committed
161
162
163
164
{
  guint attr_length;
  guint n_attributes = 0;
  guint i;
165
  guint offset;
dafydd.harries's avatar
import  
dafydd.harries committed
166
  StunAttribute *attr;
167
  StunMessage *msg = stun_message_new (STUN_MESSAGE_BINDING_REQUEST);
dafydd.harries's avatar
import  
dafydd.harries committed
168
169
170

  /* message header is 20 bytes */

171
  g_assert (length >= 20);
dafydd.harries's avatar
import  
dafydd.harries committed
172
173
174

  /* unpack the header */

175
176
  msg->type = ntohs (*(guint16 *)(s + 0));
  memcpy (msg->transaction_id, s + 4, 16);
dafydd.harries's avatar
import  
dafydd.harries committed
177
178
179

  /* count the number of attributes */

180
  for (offset = 20; offset < length; offset += attr_length)
181
    {
182
      attr_length = 4 + ntohs (*(guint16 *)(s + offset + 2));
183
184
      /* pad to multiple of 4 bytes */
      attr_length += 4 - (attr_length % 4);
185
186
      n_attributes++;
    }
dafydd.harries's avatar
import  
dafydd.harries committed
187
188
189

  /* allocate memory for the attribute list and terminate it */

190
  msg->attributes = g_malloc0 ((n_attributes + 1) * sizeof (StunAttribute *));
dafydd.harries's avatar
import  
dafydd.harries committed
191
192
193
194
  msg->attributes[n_attributes] = NULL;

  /* unpack attributes */

195
  for (i = 0, offset = 20; i < n_attributes; i++, offset += attr_length)
196
    {
197
      attr_length = 4 + ntohs (*(guint16 *)(s + offset + 2));
198
199
      attr = msg->attributes[i] = stun_attribute_unpack (attr_length,
          s + offset);
200
201
      /* pad to multiple of 4 bytes */
      attr_length += 4 - (attr_length % 4);
202
    }
dafydd.harries's avatar
import  
dafydd.harries committed
203
204
205
206
207

  return msg;
}

guint
208
stun_message_pack (StunMessage *msg, gchar **packed)
dafydd.harries's avatar
import  
dafydd.harries committed
209
{
210
  GString *tmp = g_string_new ("");
dafydd.harries's avatar
import  
dafydd.harries committed
211
212
  unsigned int packed_type;
  guint16 packed_length;
213
  guint length = 0;
dafydd.harries's avatar
import  
dafydd.harries committed
214

215
216
217
  if (msg->attributes)
    {
      StunAttribute **attr;
dafydd.harries's avatar
import  
dafydd.harries committed
218

219
220
221
      for (attr = msg->attributes; *attr; attr++)
        length += 4 + (*attr)->length;
    }
dafydd.harries's avatar
import  
dafydd.harries committed
222

223
224
  packed_type = htons (msg->type);
  packed_length = htons (length);
dafydd.harries's avatar
import  
dafydd.harries committed
225

226
227
228
229
230
231
  g_string_append_printf (tmp, "%c%c%c%c",
    ((gchar *) &packed_type)[0],
    ((gchar *) &packed_type)[1],
    ((gchar *) &packed_length)[0],
    ((gchar *) &packed_length)[1]);
  g_string_append_len (tmp, msg->transaction_id, 16);
dafydd.harries's avatar
import  
dafydd.harries committed
232

233
234
235
236
237
238
239
240
241
242
243
  if (msg->attributes)
    {
      StunAttribute **attr;

      for (attr = msg->attributes; *attr; attr++)
        {
          gchar *attr_packed;
          guint attr_length = stun_attribute_pack (*attr, &attr_packed);
          g_string_append_len (tmp, attr_packed, attr_length);
          g_free (attr_packed);
        }
dafydd.harries's avatar
import  
dafydd.harries committed
244
245
    }

246
  *packed = g_string_free (tmp, FALSE);
247
  return length + 20;
dafydd.harries's avatar
import  
dafydd.harries committed
248
249
250
251
252
253
}

gchar *
stun_message_dump (StunMessage *msg)
{
  StunAttribute **attr;
254
  GString *tmp = g_string_new ("");
dafydd.harries's avatar
import  
dafydd.harries committed
255
256
257
258
259
260
  const gchar *name;

  switch (msg->type) {
    case STUN_MESSAGE_BINDING_REQUEST:
      name = "BINDING-REQUEST";
      break;
261
262
263
    case STUN_MESSAGE_BINDING_RESPONSE:
      name = "BINDING-RESPONSE";
      break;
dafydd.harries's avatar
import  
dafydd.harries committed
264
265
266
267
    default:
      return NULL;
  }

268
  g_string_printf (tmp,
dafydd.harries's avatar
import  
dafydd.harries committed
269
270
    "%s %08x:%08x:%08x:%08x",
      name,
271
272
273
274
      *(guint32 *)(msg->transaction_id),
      *(guint32 *)(msg->transaction_id + 4),
      *(guint32 *)(msg->transaction_id + 8),
      *(guint32 *)(msg->transaction_id + 12));
dafydd.harries's avatar
import  
dafydd.harries committed
275

276
277
278
279
280
281
282
  if (msg->attributes)
    for (attr = msg->attributes; *attr; attr++)
      {
          gchar *dump = stun_attribute_dump (*attr);
          g_string_append_printf (tmp, "\n  %s", dump);
          g_free (dump);
      }
dafydd.harries's avatar
import  
dafydd.harries committed
283

284
  return g_string_free (tmp, FALSE);
dafydd.harries's avatar
import  
dafydd.harries committed
285
286
}