A super trimmed down TLS stack, GPL licensed

Dependents:   MiniTLS-HTTPS-Example

MiniTLS - A super trimmed down TLS/SSL Library for embedded devices Author: Donatien Garnier Copyright (C) 2013-2014 AppNearMe Ltd

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

tls/tls_socket.c

Committer:
MiniTLS
Date:
2014-06-10
Revision:
4:cbaf466d717d
Parent:
3:eb324ffffd2b

File content as of revision 4:cbaf466d717d:

/*
MiniTLS - A super trimmed down TLS/SSL Library for embedded devices
Author: Donatien Garnier
Copyright (C) 2013-2014 AppNearMe Ltd

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*//**
 * \file tls_socket.c
 * \copyright Copyright (c) AppNearMe Ltd 2013
 * \author Donatien Garnier
 */

#define __DEBUG__ 4
#ifndef __MODULE__
#define __MODULE__ "tls_socket.c"
#endif


#include "core/fwk.h"
#include "inc/minitls_errors.h"

#include "tls_socket.h"

minitls_err_t tls_socket_init(tls_socket_t* socket, minitls_t* minitls,
    uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer, size_t read_buffer_size)
{
  socket->minitls = minitls;

  socket->events = NULL;

  socket->read_buffer = NULL;
  buffer_init(&socket->write_buffer, write_buffer, write_buffer_size);

  socket->session.session_id_length = 0;

  minitls_err_t ret = tls_record_init(&socket->record, socket, read_buffer, read_buffer_size);
  if(ret)
  {
    return ret;
  }

  ret = tls_handshake_init(&socket->handshake, socket);
  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}

minitls_err_t tls_socket_connect(tls_socket_t* socket, const char* hostname, uint16_t port, int timeout)
{
  minitls_err_t ret = tls_record_connect(&socket->record, hostname, port);
  if(ret)
  {
    ERR("Could not connect");
    return ret;
  }

  //TODO we could allocate the handshake structure here to save memory

  ret = tls_handshake_start(&socket->handshake);
  if(ret)
  {
    ERR("Could not start handshake");
    return ret;
  }

  //Now wait for handshake to finish processing (or fail)
  while(!tls_handshake_is_done(&socket->handshake))
  {
    DBG("Handshaking...");
    ret = tls_record_process(&socket->record);
    if(ret)
    {
      ERR("Processing error");
      return ret;
    }
  }

  DBG("Connected");

  return MINITLS_OK;
}

void tls_socket_copy_session(tls_socket_t* to, tls_socket_t* from)
{
  memcpy(&to->session, &from->session, sizeof(tls_session_t));
}

//Events API -- select like
minitls_err_t tls_socket_event_list_init(tls_socket_event_list_t* list)
{
  list->head = NULL;

  //Create semaphore
/*  list->sem = rtos_sem_create(1, 0);*/
  /*if(list->sem == NULL)
  {
    return TLS_ERR_MEMORY;
  }*/ //FIXME

  return MINITLS_OK;
}

minitls_err_t tls_socket_event_init_and_register(tls_socket_event_t* event, tls_socket_event_list_t* list, tls_socket_t* socket, bool read, bool write)
{
  //Init event
  event->socket = socket;
  event->read = read;
  event->write = write;
  event->list = list;
  event->socket_list_next = NULL;
  event->event_list_next = NULL;

  //Add to select list (tail)
  if(list->head != NULL)
  {
    tls_socket_event_t* previous_event = list->head;
    while( previous_event->event_list_next != NULL )
    {
      previous_event = previous_event->event_list_next;
    }
    previous_event->event_list_next = event;
  }
  else
  {
    list->head = event;
  }

  //Add to socket's event list (tail)
  if(socket->events != NULL)
  {
    tls_socket_event_t* previous_event = socket->events;
    while( previous_event->socket_list_next != NULL )
    {
      previous_event = previous_event->socket_list_next;
    }
    previous_event->socket_list_next = event;
  }
  else
  {
    socket->events = event;
  }

  return MINITLS_OK;
}

minitls_err_t tls_socket_event_list_wait(tls_socket_event_list_t* list, int timeout) //All events unregistered at the end of wait, timeout in ms, -1 for infinite timeout
{/*
  if( rtos_sem_get(list->sem, timeout) ) //FIXME
  {*/
    return MINITLS_OK;
  /*}
  else
  {
    return TLS_ERR_TIMEOUT;
  }*/
}

//These calls are non-blocking
minitls_err_t tls_socket_read(tls_socket_t* socket, uint8_t* bytes, size_t max_size, size_t* read_size)
{
  DBG("Reading at most %d bytes", max_size);
  if(socket->read_buffer == NULL) //Socket is not ready
  {
    WARN("Socket is not ready or has been closed");
    *read_size = 0;
    return MINITLS_OK;
  }

  size_t length = buffer_length(socket->read_buffer);
  *read_size = MIN(max_size, length);

  buffer_nbytes_read(socket->read_buffer, bytes, *read_size);

  DBG("%d bytes read", *read_size);

  //If buffer has been fully read, invalidate read_buffer
  if( buffer_length(socket->read_buffer) == 0 )
  {
    socket->read_buffer = NULL;
  }

  return MINITLS_OK;
}

minitls_err_t tls_socket_write(tls_socket_t* socket, uint8_t* bytes, size_t size, size_t* written_size)
{
  size_t length = buffer_space(&socket->write_buffer);
  *written_size = MIN(size, length);

  buffer_nbytes_write(&socket->write_buffer, bytes, *written_size);

  return MINITLS_OK;
}

minitls_err_t tls_socket_flush_read(tls_socket_t* socket, int timeout)
{
  DBG("Get new bytes");
  if( socket->read_buffer != NULL ) //No need to read more bytes
  {
    WARN("Buffer is already non-empty");
    return MINITLS_OK;
  }

  tls_record_set_read_timeout(&socket->record, timeout);

  int ret;
  do
  {
    DBG("Processing TLS packet");
    ret = tls_record_process(&socket->record);
    if(ret)
    {
      return ret;
    }
  } while( socket->read_buffer == NULL );

  DBG("Buffer has been filled");
  return MINITLS_OK;
}

minitls_err_t tls_socket_flush_write(tls_socket_t* socket, int timeout)
{
  if(buffer_length(&socket->write_buffer) == 0)
  {
    return MINITLS_OK; //Useless to flush
  }

  tls_record_set_write_timeout(&socket->record, timeout);

  //Pass buffer to record layer
  minitls_err_t ret = tls_record_send(&socket->record, TLS_APPLICATION_DATA, &socket->write_buffer);

  //TODO: Advertise writeable here

  if(ret)
  {
    return ret;
  }

  return MINITLS_OK;
}

//Called from record layer
minitls_err_t tls_socket_readable_callback(tls_socket_t* socket, buffer_t* buffer)
{
  socket->read_buffer = buffer;

  //Process events

  return MINITLS_OK;
}

minitls_err_t tls_socket_close(tls_socket_t* socket)
{
  minitls_err_t ret = tls_record_close(&socket->record);
  if(ret)
  {
    return ret;
  }
  return MINITLS_OK;
}