summaryrefslogtreecommitdiff
path: root/lib/gnutls_dtls.c
blob: d0934a516f5311caac46ec6585eff1c332cc10ad (plain)
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * Copyright (C) 2009 Free Software Foundation (copyright assignement pending)
 *
 * Author: Jonathan Bastien-Filiatrault
 *
 * This file is part of GNUTLS.
 *
 * The GNUTLS 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 Street, Fifth Floor, Boston, MA 02110-1301,
 * USA
 *
 */

/* Functions that relate to DTLS retransmission and reassembly.
 */

#include "gnutls_int.h"
#include "gnutls_errors.h"
#include "debug.h"
#include "gnutls_dtls.h"
#include "gnutls_record.h"

/* This function is called once a handshake message is ready to be
 * queued in the next outgoing flight. The actual transmission occurs
 * in _gnutls_dtls_transmit.
 *
 * This function is called from the handshake layer.
 */
int
_gnutls_dtls_handshake_enqueue (gnutls_session_t session,
				opaque *data,
				uint32_t datasize,
				gnutls_handshake_description_t type,
				uint16_t sequence)
{
  dtls_hsk_retransmit_buffer *msg;

  msg = gnutls_malloc (sizeof(dtls_hsk_retransmit_buffer));
  if (msg == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  msg->msg.size = datasize - DTLS_HANDSHAKE_HEADER_SIZE;

  msg->msg.data = gnutls_malloc (msg->msg.size);
  if (msg->msg.data == NULL)
    {
      gnutls_free (msg);
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  memcpy (msg->msg.data, data + DTLS_HANDSHAKE_HEADER_SIZE, msg->msg.size);

  msg->next = NULL;
  /* FIXME: dummy epoch */
  msg->epoch = 0;
  msg->type = type;
  msg->sequence = sequence;

  _gnutls_dtls_log ("DTLS[%p]: Enqueued Packet[%u] %s(%d) with length: %u\n",
		    session, (uint)sequence, _gnutls_handshake2str (type),
		    type, msg->msg.size);

  *(session->internals.dtls.retransmit_end) = msg;
  session->internals.dtls.retransmit_end = &msg->next;

  return 0;
}

/* This function fragments and transmits a previously buffered
 * outgoing message. */
static inline int
transmit_message (gnutls_session_t session,
		  dtls_hsk_retransmit_buffer *msg)
{
  opaque *data;
  uint offset, frag_len;
  const uint mtu = session->internals.dtls.hsk_mtu;

  data = gnutls_malloc (DTLS_HANDSHAKE_HEADER_SIZE + mtu);
  if (data == NULL)
    {
      gnutls_assert ();
      return GNUTLS_E_MEMORY_ERROR;
    }

  /* Write fixed headers
   */

  /* Handshake type */
  data[0] = (uint8_t) msg->type;

  /* Total length */
  _gnutls_write_uint24 (msg->msg.size, &data[1]);

  /* Handshake sequence */
  _gnutls_write_uint16 (msg->sequence, &data[4]);

  /* Chop up and send handshake message into mtu-size pieces. */
  for (offset=0; offset < msg->msg.size; offset += mtu)
    {
      /* Calculate fragment length */
      if(offset + mtu > msg->msg.size)
	frag_len = msg->msg.size - offset;
      else
	frag_len = mtu;

      /* Fragment offset */
      _gnutls_write_uint24 (offset, &data[6]);

      /* Fragment length */
      _gnutls_write_uint24 (frag_len, &data[9]);

      memcpy (&data[12], msg->msg.data + offset, frag_len);

      _gnutls_dtls_log ("DTLS[%p]: Sending Packet[%u] fragment %s(%d) with "
			"length: %u, offset: %u, fragment length: %u\n",
			session, msg->sequence,
			_gnutls_handshake2str (msg->type),
			msg->type, msg->msg.size, offset, frag_len);

      /* FIXME: We should collaborate with the record layer to pack as
	 many records possible into a single datagram. We should also
	 tell the record layer which epoch to use for encryption. */
      _gnutls_send_int (session, GNUTLS_HANDSHAKE, msg->type, EPOCH_WRITE_CURRENT,
			data, DTLS_HANDSHAKE_HEADER_SIZE + frag_len, 0);
   }

  gnutls_free (data);
  return 0;
}

/* This function transmits the flight that has been previously
 * buffered.
 *
 * This function is called from the handshake layer and calls the
 * record layer.
 */
int
_gnutls_dtls_transmit (gnutls_session_t session)
{
  /* PREPARING -> SENDING state transition */
  dtls_hsk_retransmit_buffer *msg;

  _gnutls_dtls_log ("DTLS[%p]: Start of flight transmission.\n", session);

  for (msg = session->internals.dtls.retransmit; msg != NULL; msg = msg->next)
    transmit_message (session, msg);
  _gnutls_io_write_flush (session);


  _gnutls_dtls_log ("DTLS[%p]: End of flight transmission.\n", session);

  _gnutls_dtls_clear_outgoing_buffer (session);

  /* SENDING -> WAITING state transition */
  return 0;
}

/* This function clears the outgoing flight buffer. */
void
_gnutls_dtls_clear_outgoing_buffer (gnutls_session_t session)
{
  dtls_hsk_retransmit_buffer *msg, *next;

  _gnutls_dtls_log ("DTLS[%p]: Clearing outgoing buffer.\n", session);

  for (msg = session->internals.dtls.retransmit; msg != NULL;)
    {
      next = msg->next;

      gnutls_free (msg->msg.data);
      gnutls_free (msg);

      msg = next;
    }

  session->internals.dtls.retransmit_end = &session->internals.dtls.retransmit;
  session->internals.dtls.retransmit = NULL;
}

void
_gnutls_dtls_split_sequence (const uint64 *input,
			     uint16_t *epoch, uint64_t *sequence)
{
  *epoch = _gnutls_read_uint16 (UINT64DATA(*input));
  *sequence = _gnutls_read_uint48 (&UINT64DATA(*input)[2]);

  fprintf(stderr, "%04x:%012lx\n", *epoch, *sequence);
}