tport_stub_stun.c 8.42 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2006 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE tport_stub_stun.c Stub interface for STUN
 *
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 *
 * @date Created: Fri Mar 31 12:31:36 EEST 2006
 */

#include "config.h"

#include <sofia-sip/stun.h>
#include <sofia-sip/su_tagarg.h>

#define TPORT_STUN_SERVER_T stun_mini_t
#include "tport_internal.h"
39 40
#include "sofia-sip/msg_buffer.h"
#include "sofia-sip/msg_addr.h"
41 42 43

#include <assert.h>

44 45 46 47 48 49
/* ---------------------------------------------------------------------- */
/* Plugin pointer */

tport_stun_server_vtable_t const *tport_stun_server_vtable = NULL;


50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
static
tport_stun_server_t *vst_create(su_root_t *root, tagi_t const *tags)
{
  return stun_mini_create();
}

static
tport_stun_server_vtable_t const stun_mini_vtable = 
  {
    sizeof stun_mini_vtable,
    vst_create, 
    stun_mini_destroy,
    stun_mini_add_socket, 
    stun_mini_remove_socket, 
    stun_mini_request
  };

/** Initialize stun server */
int tport_init_stun_server(tport_master_t *mr, tagi_t const *tags)
{
  tport_stun_server_vtable_t const *vst = tport_stun_server_vtable;

  if (vst == NULL)
73
    /* Nobody has plugged better server in, use miniserver */
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    tport_stun_server_vtable = vst = &stun_mini_vtable;

  if (!vst)
    return 0;

  if (mr->mr_params->tpp_stun_server)
    mr->mr_stun_server = vst->vst_create(mr->mr_root, tags);
  mr->mr_master->tp_has_stun_server = mr->mr_stun_server != NULL;
  return 0;
}

/** Deinit stun server */
void tport_deinit_stun_server(tport_master_t *mr)
{
  tport_stun_server_vtable_t const *vst = tport_stun_server_vtable;

  if (mr->mr_stun_server) {
    assert(vst);
    vst->vst_destroy(mr->mr_stun_server), mr->mr_stun_server = NULL;
  }
}

int tport_stun_server_add_socket(tport_t *tp)
{
  tport_stun_server_t *stun_server = tp->tp_master->mr_stun_server;

  if (tport_stun_server_vtable && 
      stun_server &&
      tp->tp_params->tpp_stun_server) {
    if (tport_stun_server_vtable->vst_add_socket(stun_server, 
						 tp->tp_socket) == 0)
      tp->tp_has_stun_server = 1;
  }
  return 0;
}

int tport_stun_server_remove_socket(tport_t *tp)
{
  tport_stun_server_t *stun_server = tp->tp_master->mr_stun_server;

  if (tport_stun_server_vtable &&
      stun_server &&
      tp->tp_has_stun_server) {
    tport_stun_server_vtable->vst_remove_socket(stun_server, tp->tp_socket);
    tp->tp_has_stun_server = 0;
  }
  return 0;
}

123
/**Process stun messagee.
124 125
 *
 * @retval -1 error
126
 * @retval 3  stun message received, ignore  
127
 */
128
int tport_recv_stun_dgram(tport_t const *self,
129 130 131
			  msg_t **in_out_msg,
			  su_sockaddr_t *from,
			  socklen_t fromlen)
132
{
133 134 135 136
  int retval = -1;
  msg_t *msg;
  uint8_t *request;
  size_t n;
137

138 139 140
  assert(in_out_msg); assert(*in_out_msg);

  msg = *in_out_msg;
141

142 143 144 145 146 147 148 149
  request = msg_buf_committed_data(msg);
  n = msg_buf_committed(msg);

  if (n < 20 || request == NULL) {
    su_seterrno(EBADMSG);
    retval = -1;
  }
  else if (request[0] == 1) {
150 151
    /* This is a response. */
    if (self->tp_pri->pri_vtable->vtp_stun_response) {
152
      if (self->tp_pri->pri_vtable->vtp_stun_response(self, request, n, 
153 154 155
						      from, fromlen) < 0)
	retval = -1;
    }
156
    else
157
      SU_DEBUG_7(("tport(%p): recv_stun_dgram(): "
158
		  "ignoring request with "MOD_ZU" bytes\n", (void *)self, n));
159
  }
160
  else if (request[0] == 0 && self->tp_master->mr_stun_server) {
161 162
    tport_stun_server_vtable_t const *vst = tport_stun_server_vtable;
    vst->vst_request(self->tp_master->mr_stun_server,
163
		     self->tp_socket, request, n, 
164 165
		     (void *)from, fromlen);
  }
166 167 168 169
  else if (request[0] == 0) {
    /* Respond to stun request with a simple error message. */
    int const status = 600;
    char const *error = "Not Implemented";
170 171
    size_t unpadded = strlen(error);
    uint16_t elen;
172
    uint8_t dgram[128];
173

174 175 176 177 178 179
    if (unpadded > sizeof(dgram) - 28)
      unpadded = sizeof(dgram) - 28;

    elen = (uint16_t)unpadded;
    elen = (elen + 3) & -4;	/* Round up to 4 */

180
    SU_DEBUG_7(("tport(%p): recv_stun_dgram(): "
181
		"responding %u %s\n", (void *)self, status, error));
182
  /*
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
     0                   1                   2                   3
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |      STUN Message Type        |         Message Length        |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                             Transaction ID
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                                                    |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    */

#define set16(b, offset, value)			\
  (((b)[(offset) + 0] = ((value) >> 8) & 255),	\
   ((b)[(offset) + 1] = (value) & 255))

    /* Respond to request */
    dgram[0] = 1; /* Mark as response */
204
    dgram[1] = request[1] | 0x10; /* Mark as error response */
205
    set16(dgram, 2, elen + 4 + 4);
206

207
    /* TransactionID is there at bytes 4..19 */
208 209
    memcpy(dgram + 4, request + 4, 16);

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    /*
    TLV At 20:
     0                   1                   2                   3
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |         Type                  |            Length             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    */
    set16(dgram, 20, 0x0009); /* ERROR-CODE */
    set16(dgram, 22, elen + 4);
    /*
    ERROR-CODE at 24:
     0                   1                   2                   3
     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
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                   0                     |Class|     Number    |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |      Reason Phrase (variable)                                ..
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     */
    dgram[24] = 0, dgram[25] = 0;
    dgram[26] = status / 100, dgram[27] = status % 100;
232 233
    memcpy(dgram + 28, error, unpadded);
    memset(dgram + 28 + unpadded, 0, elen - unpadded);
234

235 236
    sendto(self->tp_socket, (void *)dgram, 28 + elen, 0,
	   (void *)from, fromlen);
237 238
#undef set16
  }
239
  else {
240
    SU_DEBUG_0(("tport(%p): recv_stun_dgram(): internal error\n", (void *)self));
241 242 243
    su_seterrno(EBADMSG);
    retval = -1;
  }
244

245
  *in_out_msg = NULL, msg_destroy(msg);
246 247 248 249

  return retval;
}

250

251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
/** Activate (STUN) keepalive for transport */
int tport_keepalive(tport_t *tp, su_addrinfo_t const *ai,
		    tag_type_t tag, tag_value_t value, ...)
{
  if (tp && tp->tp_pri && tp->tp_pri->pri_vtable->vtp_keepalive) {
    int retval;
    ta_list ta;
    ta_start(ta, tag, value);
    retval = tp->tp_pri->pri_vtable->vtp_keepalive(tp, ai, ta_args(ta));
    ta_end(ta);
    return retval;
  }
  return -1;
}

/* ---------------------------------------------------------------------- */
/* Plugin interface */

/** Plug in stun server.
 *
 * @note This function @b must be called before any transport is initialized.
 */
int tport_plug_in_stun_server(tport_stun_server_vtable_t const *vtable)
{
  if (!vtable)
    return 0;

278
  if (vtable->vst_size <= (int)sizeof *vtable)
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
    return su_seterrno(EINVAL);

  if (!vtable->vst_create || 
      !vtable->vst_destroy ||
      !vtable->vst_add_socket ||
      !vtable->vst_remove_socket ||
      !vtable->vst_request)
    return su_seterrno(EFAULT);

  if (tport_stun_server_vtable)
    return su_seterrno(EEXIST);

  tport_stun_server_vtable = vtable;

  return 0;
}