/*

                                   _JNJ`                        
                               .JNMH`                           
                              JMMF`       `;.                   
                            .NMM)          `MN.                 
                            MMM)            (MML                
                           (MMM`             MMML               
                     M  I  D  N  I  G  H  T    C  o  D  E       
                           (NMMF             MHNH               
                            NMML            .MMM                
                             NMML          .NMH                 
                              4MMNL       .#F                   
                               `4HNNL_   `                      
                                   `"""`                        

                          Copyright (C) 2004-2006
       "Ian (Larry) Latter" <ian dot latter at midnightcode dot org>

    Midnight Code is a registered trademark of Ian Latter.

    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, and mirrored at the Midnight Code web
    site; as at version 2 of the License only.
  
    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
    (version 2) along with this program; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA, or see http://midnightcode.org/gplv2.txt

*/

/* midnightcode-socket.c :: Midnight Code socket library		*/


#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>

#include "preferences.h"
#include "libmidnightcode.h"
#include "midnightcode-list.h"
#include "midnightcode-base64.h"
#include "midnightcode-parse.h"
#include "midnightcode-socket.h"



struct midnightcode_socket_head_t * midnightcode_socket_sockets = NULL;
pthread_mutex_t midnightcode_socket_mutex_t = PTHREAD_MUTEX_INITIALIZER;
pthread_t midnightcode_socket_pthread_t;


/* NOTE: _RAW = 10.93Mbytes/sec  _MIDNIGHTCODE = 4.23Mbytes/sec */
int
midnightcode_socket_open(struct midnightcode_socket_element_t ** socket_u,
                         int control, int dead_peer_detection,
                         int max_connections, int direction,
                         int protocol, char * host, int port) {
  struct midnightcode_socket_head_t * sockets;
  struct midnightcode_socket_element_t * sock;
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_head_t * send_control_queue;
  struct midnightcode_socket_queue_head_t * send_data_queue;
  struct midnightcode_socket_queue_head_t * receive_control_queue;
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  int socket_options;
  int socket_send_size;
  int socket_recv_size;
  int listen_queue_len;
  int dpd_interval;
  int ver_interval;
  struct sockaddr_in sockaddr;
  struct hostent *host_entry;


  pthread_mutex_lock(&midnightcode_socket_mutex_t);

  if(!midnightcode_socket_sockets) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  sockets = midnightcode_socket_sockets;
  if(!host) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(max_connections < 0 || port < 0) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(control != MIDNIGHTCODE_SOCKET_CONTROL_RAW &&
    control != MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(dead_peer_detection != MIDNIGHTCODE_SOCKET_DPD_DISABLE &&
    dead_peer_detection != MIDNIGHTCODE_SOCKET_DPD_ENABLE) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(direction != MIDNIGHTCODE_SOCKET_DIR_INITIATE &&
    direction != MIDNIGHTCODE_SOCKET_DIR_LISTEN &&
    direction != MIDNIGHTCODE_SOCKET_DIR_RECEIVE) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(protocol != MIDNIGHTCODE_SOCKET_PROTOCOL_TCP &&
    protocol != MIDNIGHTCODE_SOCKET_PROTOCOL_UDP) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }

  sock = malloc(sizeof(struct midnightcode_socket_element_t));
  if(!sock) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(sock, 0, sizeof(struct midnightcode_socket_element_t));
  receive_chunks = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_chunks) {
    free(sock);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(receive_chunks, 0, sizeof(midnightcode_socket_queue_head_t));
  send_control_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!send_control_queue) {
    free(sock);
    free(receive_chunks);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(send_control_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  send_data_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!send_data_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(send_data_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  receive_control_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_control_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    free(send_data_queue);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(receive_control_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  receive_data_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_data_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    free(send_data_queue);
    free(receive_control_queue);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(receive_data_queue, 0, sizeof(midnightcode_socket_queue_head_t));

  sock->receive_chunks = receive_chunks;
  sock->send_control_queue = send_control_queue;
  sock->receive_control_queue = receive_control_queue;
  sock->send_data_queue = send_data_queue;
  sock->receive_data_queue = receive_data_queue;

  socket_recv_size = MIDNIGHTCODE_SOCKET_RECEIVE_BLOCK;
  socket_send_size = MIDNIGHTCODE_SOCKET_SEND_BLOCK;
  listen_queue_len = MIDNIGHTCODE_SOCKET_INBOUND_MAX_Q;
  dpd_interval = MIDNIGHTCODE_SOCKET_DPD_INTERVAL *
    (1000000 / MIDNIGHTCODE_SOCKET_POLL_TIME_HANDLER);
  ver_interval = MIDNIGHTCODE_SOCKET_VERSION_INTERVAL *
    (1000000 / MIDNIGHTCODE_SOCKET_POLL_TIME_HANDLER);

  sock->control = control;
  sock->direction = direction;
  sock->protocol = protocol;
  sock->state = MIDNIGHTCODE_SOCKET_STATE_INITIALISE;

  sock->dpd_enabled = 0;
  sock->dpd_interval = 0;
  sock->dpd_fails = 0;
  sock->dpd_max_interval = 0;
  sock->dpd_max_fails = 0;

  sock->version = MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN;
  sock->ver_interval = 0;
  sock->ver_requests = 0;
  sock->ver_max_interval = 0;
  sock->ver_max_requests = 0;

  sock->invalid_notifies = 0;
  sock->invalid_blocks = 0;

  switch(sock->protocol) {
    case MIDNIGHTCODE_SOCKET_PROTOCOL_TCP:
      sock->socket = socket(PF_INET, SOCK_STREAM, 0);
      break;

    case MIDNIGHTCODE_SOCKET_PROTOCOL_UDP:
      sock->socket = socket(PF_INET, SOCK_DGRAM, 0);
      break;

    default:
      sock->socket = -1;
      break;
  }
  if(sock->socket < 0) {
    midnightcode_socket_closeshut(sockets, sock,
      MIDNIGHTCODE_SOCKET_STATE_DESTROY);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }

  switch(sock->protocol) {
    case MIDNIGHTCODE_SOCKET_PROTOCOL_TCP:
      if(setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF,
        (char *)&socket_send_size, (int)sizeof(socket_send_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      if(setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF,
          (char *)&socket_recv_size, (int)sizeof(socket_recv_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      break;

    case MIDNIGHTCODE_SOCKET_PROTOCOL_UDP:
      if(setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF,
        (char *)&socket_send_size, (int)sizeof(socket_send_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      if(setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF,
          (char *)&socket_recv_size, (int)sizeof(socket_recv_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      break;

    default:
      break;
  }

  socket_options = fcntl(sock->socket, F_GETFL, NULL);
  socket_options |= O_NONBLOCK;
  fcntl(sock->socket, F_SETFL, socket_options);

  host_entry = NULL;
  if(host) {
    if((host_entry = gethostbyname(host)) == NULL) {
      close(sock->socket);
      midnightcode_socket_closeshut(sockets, sock,
        MIDNIGHTCODE_SOCKET_STATE_DESTROY);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
  }

  memset(&sockaddr, 0, sizeof(struct sockaddr_in));
  sockaddr.sin_family = AF_INET;

  switch(sock->direction) {
    case MIDNIGHTCODE_SOCKET_DIR_INITIATE:
      if(!host) {
        close(sock->socket);
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      sock->port_remote = port;
      memcpy(&sock->ipv4_remote, host_entry->h_addr, sizeof(struct in_addr));
      snprintf(sock->ipv4_remote_s, 32, "%s", inet_ntoa(sock->ipv4_remote));
      sockaddr.sin_addr.s_addr = sock->ipv4_remote.s_addr;
      sockaddr.sin_port = htons(sock->port_remote);
      if(connect(sock->socket, (struct sockaddr *)&sockaddr,
        sizeof(sockaddr)) < 0 && errno != EINPROGRESS) {
        close(sock->socket);
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      /* NOTE: Added for recvfrom support only */
      sock->ipv4_peer.sin_family = AF_INET;
      sock->ipv4_peer.sin_addr.s_addr = sock->ipv4_remote.s_addr;
      sock->ipv4_peer.sin_port = htons(sock->port_remote);
      break;

    case MIDNIGHTCODE_SOCKET_DIR_LISTEN:
      sock->max_connections = max_connections;
      if(sock->max_connections < listen_queue_len)
        sock->max_connections = listen_queue_len;
      sock->port_local = port;
      if(host) {
        memcpy(&sock->ipv4_local, host_entry->h_addr, sizeof(struct in_addr));
      } else {
        sock->ipv4_local.s_addr = INADDR_ANY;
      }
      snprintf(sock->ipv4_local_s, 32, "%s", inet_ntoa(sock->ipv4_local));
      sockaddr.sin_addr.s_addr = sock->ipv4_local.s_addr;
      sockaddr.sin_port = htons(sock->port_local);
      if(bind(sock->socket, (struct sockaddr *)&sockaddr,
        sizeof(sockaddr)) < 0) {
        close(sock->socket);
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      if(sock->protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {
        if(listen(sock->socket, listen_queue_len) < 0) {
          close(sock->socket);
          midnightcode_socket_closeshut(sockets, sock,
            MIDNIGHTCODE_SOCKET_STATE_DESTROY);
          pthread_mutex_unlock(&midnightcode_socket_mutex_t);
          return -1;
        }
      }
      break;

    default:
      close(sock->socket);
      midnightcode_socket_closeshut(sockets, sock,
        MIDNIGHTCODE_SOCKET_STATE_DESTROY);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
      break;
  }

  switch(sock->control) {
    case MIDNIGHTCODE_SOCKET_CONTROL_RAW:
      if(dead_peer_detection == MIDNIGHTCODE_SOCKET_DPD_ENABLE) {
        close(sock->socket);
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
      break;

    case MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE:
      if(dead_peer_detection == MIDNIGHTCODE_SOCKET_DPD_ENABLE) {
        sock->dpd_enabled = 1;
        sock->dpd_interval = dpd_interval;
        sock->dpd_fails = 0;
        sock->dpd_max_interval = dpd_interval;
        sock->dpd_max_fails = 5;
      }
      sock->version = MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN;
      sock->ver_interval = ver_interval;
      sock->ver_requests = 0;
      sock->ver_max_interval = ver_interval;
      sock->ver_max_requests = 5;
      break;

    default:
      close(sock->socket);
      midnightcode_socket_closeshut(sockets, sock,
        MIDNIGHTCODE_SOCKET_STATE_DESTROY);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
      break;
  }

  sock->state = MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL;

  midnightcode_list_append(sockets, sock);

  *socket_u = sock;

  if(sock->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE &&
    sock->direction == MIDNIGHTCODE_SOCKET_DIR_INITIATE) {
    midnightcode_socket_control_dispatch(
      sock, MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP, NULL, 0);
  }

  pthread_mutex_unlock(&midnightcode_socket_mutex_t);

  return 0;
}



/* TODO: SEGV under TFTP client on read, but not read_block */
int
midnightcode_socket_read(struct midnightcode_socket_element_t * socket,
                         char ** buffer, int * buffer_len, int io) {
  int ret_i;


  ret_i = 0;
  ret_i = midnightcode_socket_read_block(socket, buffer, buffer_len, 0, io);


  return ret_i;
}



int
midnightcode_socket_read_block(struct midnightcode_socket_element_t * socket,
                                char ** buffer, int * buffer_len,
                                int block_count, int io) {
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  struct midnightcode_socket_queue_element_t * receive_data;
  struct midnightcode_socket_queue_element_t * old_element;
  char * block_buffer;
  int block_buffer_size;
  int buffer_offset;
  int io_block;
  int ret_i;


  if(io != MIDNIGHTCODE_SOCKET_IO_BLOCKING &&
    io != MIDNIGHTCODE_SOCKET_IO_NONBLOCKING)
    return -1;
  if(block_count < 0)
    return -1;
  if(!socket)
    return -1;

  io_block = 1;
  while(io_block) {

    pthread_mutex_lock(&midnightcode_socket_mutex_t);

    if(socket->state != MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL) {
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
    if(!socket->receive_data_queue) {
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }

    receive_data_queue = socket->receive_data_queue;

    /* TODO: Consider IO blocking time-out */
    if(midnightcode_list_count(receive_data_queue) == 0) {

      if(io == MIDNIGHTCODE_SOCKET_IO_NONBLOCKING) {
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return 0;
      } else {
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      }

    } else {

      io_block = 0;

    }
  }

  buffer_offset = *buffer_len;
  ret_i = 0;

  midnightcode_list_for_each(receive_data, receive_data_queue) {

    if(receive_data->buffer_size <= 0) {
      old_element = receive_data;
      midnightcode_list_remove(receive_data_queue, receive_data);
      if(old_element != NULL) {
        if(old_element->buffer != NULL)
          free(old_element->buffer);
        free(old_element);
      }
      continue;
    }

    if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
      block_buffer_size =
        midnightcode_socket_membrane_size_est(MIDNIGHTCODE_SOCKET_CODED_DECODE,
        MIDNIGHTCODE_SOCKET_PACKET_DATA, receive_data->buffer_size);
      if(block_buffer_size <= 0) {
        old_element = receive_data;
        midnightcode_list_remove(receive_data_queue, receive_data);
        if(old_element != NULL) {
          if(old_element->buffer != NULL)
            free(old_element->buffer);
          free(old_element);
        }
        continue;
      }
      block_buffer = malloc(block_buffer_size);
      if(!(block_buffer)) {
        old_element = receive_data;
        midnightcode_list_remove(receive_data_queue, receive_data);
        if(old_element != NULL) {
          if(old_element->buffer != NULL)
            free(old_element->buffer);
          free(old_element);
        }
        continue;
      }
      memset(block_buffer, 0, block_buffer_size);
      block_buffer_size = midnightcode_socket_membrane(
        MIDNIGHTCODE_SOCKET_CODED_DECODE, MIDNIGHTCODE_SOCKET_PACKET_DATA,
        block_buffer, receive_data->buffer,
        block_buffer_size, receive_data->buffer_size);
      old_element = receive_data;
      midnightcode_list_remove(receive_data_queue, receive_data);
      if(old_element != NULL) {
        if(old_element->buffer != NULL)
          free(old_element->buffer);
        free(old_element);
      }
      if(block_buffer_size <= 0) {
        free(block_buffer);
        continue;
      }

    } else {

      block_buffer_size = receive_data->buffer_size;
      block_buffer = malloc(block_buffer_size);
      if(!(block_buffer)) {
        old_element = receive_data;
        midnightcode_list_remove(receive_data_queue, receive_data);
        if(old_element != NULL) {
          if(old_element->buffer != NULL)
            free(old_element->buffer);
          free(old_element);
        }
        continue;
      }
      memset(block_buffer, 0, block_buffer_size);
      memcpy(block_buffer, receive_data->buffer, block_buffer_size);
      old_element = receive_data;
      midnightcode_list_remove(receive_data_queue, receive_data);
      if(old_element != NULL) {
        if(old_element->buffer != NULL)
          free(old_element->buffer);
        free(old_element);
      }
    }

    *buffer_len += block_buffer_size;
    *buffer = realloc(*buffer, *buffer_len);
    if(!*buffer) {
      *buffer_len = 0;
      free(block_buffer);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
    buffer_offset = *buffer_len - block_buffer_size;
    memset((*buffer + buffer_offset), 0, block_buffer_size);
    memcpy((*buffer + buffer_offset),
      block_buffer, block_buffer_size);
    free(block_buffer);
 
    ret_i++;
    if(block_count > 0) {
      if(block_count == ret_i) {
        break;
      }
    }
  }

  pthread_mutex_unlock(&midnightcode_socket_mutex_t);

  return ret_i;
}



int
midnightcode_socket_write(struct midnightcode_socket_element_t * socket,
                            char * buffer, int buffer_len, int io) {
  char * block_ptr;
  int chunk_size;
  int block_buffer_size;
  int block_buffer_sent;
  int ret_i;


  if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
    chunk_size = midnightcode_socket_membrane_size_est(
      MIDNIGHTCODE_SOCKET_CODED_DECODE,
      MIDNIGHTCODE_SOCKET_PACKET_DATA,
      MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX - 20);
  } else {
    chunk_size = MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX;
  }
  if(chunk_size <= 0)
    chunk_size = 64;

  ret_i = 0;
  block_buffer_sent = 0;
  block_ptr = buffer;

  while(block_buffer_sent < buffer_len) {
    block_buffer_size = chunk_size;
    if(block_buffer_sent + block_buffer_size > buffer_len)
      block_buffer_size = buffer_len - block_buffer_sent;

    if(midnightcode_socket_write_block(socket, block_ptr,
      block_buffer_size, io) > 0) {
      ret_i++;
    } else {
      ret_i = 0 - ret_i;
      break;
    }

    block_buffer_sent += block_buffer_size;
  }


  return ret_i;
}



int
midnightcode_socket_write_block(struct midnightcode_socket_element_t * socket,
                            char * buffer, int buffer_len, int io) {
  struct midnightcode_socket_queue_head_t * send_data_queue;
  struct midnightcode_socket_queue_element_t * send_data;
  int io_block;
  int ret_i;


  if(io != MIDNIGHTCODE_SOCKET_IO_BLOCKING &&
    io != MIDNIGHTCODE_SOCKET_IO_NONBLOCKING)
    return -1;
  if(!buffer)
    return -1;
  if(buffer_len <= 0)
    return -1;
  if(!socket)
    return -1;

  io_block = 1;
  while(io_block) {

    pthread_mutex_lock(&midnightcode_socket_mutex_t);

    if(socket->state != MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL) {
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
    if(!socket->send_data_queue) {
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
    if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
      if(midnightcode_socket_membrane_size_est(
        MIDNIGHTCODE_SOCKET_CODED_ENCODE,
        MIDNIGHTCODE_SOCKET_PACKET_DATA, buffer_len) >
        MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX) {
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -1;
      }
    }

    send_data_queue = socket->send_data_queue;

    /* TODO: Consider IO blocking time-out */
    if(midnightcode_list_count(send_data_queue) >=
      MIDNIGHTCODE_SOCKET_USER_LIST_MAX) {

      if(io == MIDNIGHTCODE_SOCKET_IO_NONBLOCKING) {
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        return -2;
      } else {
        pthread_mutex_unlock(&midnightcode_socket_mutex_t);
        usleep(MIDNIGHTCODE_SOCKET_POLL_TIME);
      }

    } else {

      io_block = 0;

    }
  }

  ret_i = 0;

  send_data = malloc(sizeof(struct midnightcode_socket_queue_element_t));
  if(!send_data) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(send_data, 0, sizeof(struct midnightcode_socket_queue_element_t));

  if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
    send_data->buffer_size =
      midnightcode_socket_membrane_size_est(MIDNIGHTCODE_SOCKET_CODED_ENCODE,
      MIDNIGHTCODE_SOCKET_PACKET_DATA, buffer_len);
  } else {
    send_data->buffer_size = buffer_len;
  }
  if(send_data->buffer_size <= 0) {
    free(send_data);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  send_data->buffer = malloc(send_data->buffer_size);
  if(!(send_data->buffer)) {
    free(send_data);
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  memset(send_data->buffer, 0, send_data->buffer_size);
  if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
    send_data->buffer_size = midnightcode_socket_membrane(
      MIDNIGHTCODE_SOCKET_CODED_ENCODE, MIDNIGHTCODE_SOCKET_PACKET_DATA,
      send_data->buffer, buffer,
      send_data->buffer_size, buffer_len);
    if(send_data->buffer_size <= 0) {
      free(send_data->buffer);
      free(send_data);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
    send_data->buffer = realloc(send_data->buffer, send_data->buffer_size);
    if(!(send_data->buffer)) {
      free(send_data);
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      return -1;
    }
  } else {
    memset(send_data->buffer, 0, send_data->buffer_size);
    memcpy(send_data->buffer, buffer, send_data->buffer_size);
  }

  midnightcode_list_append(send_data_queue, send_data);
  ret_i++;

  pthread_mutex_unlock(&midnightcode_socket_mutex_t);

  return ret_i;
}



int
midnightcode_socket_close(struct midnightcode_socket_element_t * socket) {
  struct midnightcode_socket_head_t * sockets;
  int ret_i;


  pthread_mutex_lock(&midnightcode_socket_mutex_t);

  if(!socket) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }
  if(socket->state >= MIDNIGHTCODE_SOCKET_STATE_USER_SHUT) {
    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    return -1;
  }

  sockets = midnightcode_socket_sockets;
  ret_i = 1;
  if(midnightcode_socket_closeshut(sockets, socket,
    MIDNIGHTCODE_SOCKET_STATE_USER_SHUT) > 0) {
    ret_i = 0;
  }

  pthread_mutex_unlock(&midnightcode_socket_mutex_t);

  return ret_i;
}



int
midnightcode_socket_closeshut(struct midnightcode_socket_head_t * sockets,
                              struct midnightcode_socket_element_t * socket,
                              int state) {
  struct midnightcode_socket_queue_head_t * list_head;
  struct midnightcode_socket_queue_element_t * list_element;
  struct midnightcode_socket_queue_element_t * old_element;
  struct midnightcode_socket_element_t * old_socket;
  int queue_num;
  int ret_i;


  if(!socket)
    return -1;
  if(state != MIDNIGHTCODE_SOCKET_STATE_CLOSE &&
    state != MIDNIGHTCODE_SOCKET_STATE_USER_SHUT &&
    state != MIDNIGHTCODE_SOCKET_STATE_DESTROY)
    return -1;

  if(socket->state < MIDNIGHTCODE_SOCKET_STATE_CLOSE) {
    /* TODO: Consider sending a close control to the other side */
    if(close(socket->socket) < 0) {
      if(errno != EBADF) {
        close(socket->socket);
      }
    }
  }

  socket->state = state;

  if(socket->state == MIDNIGHTCODE_SOCKET_STATE_CLOSE)
    return 0;

  list_head = NULL;
  list_element = NULL;
  old_element = NULL;
  ret_i = 0;

  for(queue_num = 0; queue_num < 5; queue_num++) {

    switch(queue_num) {
      case 0:
        list_head = socket->receive_chunks;
        break;

      case 1:
        list_head = socket->receive_data_queue;
        break;

      case 2:
        list_head = socket->receive_control_queue;
        break;

      case 3:
        list_head = socket->send_data_queue;
        break;

      case 4:
        list_head = socket->send_control_queue;
        break;
    }
    if(!list_head)
      continue;

    midnightcode_list_for_each(list_element, list_head) {
      old_element = list_element;
      midnightcode_list_remove(list_head, list_element);
      if(old_element != NULL) {
        if(old_element->buffer != NULL)
          free(old_element->buffer);
        free(old_element);
      }
      ret_i++;
    }
  }

  if(socket->state == MIDNIGHTCODE_SOCKET_STATE_DESTROY) {

    if(socket->receive_chunks != NULL)
      free(socket->receive_chunks);
    socket->receive_chunks = NULL;
    if(socket->receive_data_queue != NULL)
      free(socket->receive_data_queue);
    socket->receive_data_queue = NULL;
    if(socket->receive_control_queue != NULL)
      free(socket->receive_control_queue);
    socket->receive_control_queue = NULL;
    if(socket->send_data_queue != NULL)
      free(socket->send_data_queue);
    socket->send_data_queue = NULL;
    if(socket->send_control_queue != NULL)
      free(socket->send_control_queue);
    socket->send_control_queue = NULL;

    old_socket = socket;
    midnightcode_list_remove(sockets, socket);
    if(old_socket != NULL)
      free(old_socket);
  }
  ret_i++;


  return ret_i;
}



int
midnightcode_socket_accept(struct midnightcode_socket_head_t * sockets,
                           struct midnightcode_socket_element_t * parent) {
  struct midnightcode_socket_element_t * sock;
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_head_t * send_control_queue;
  struct midnightcode_socket_queue_head_t * send_data_queue;
  struct midnightcode_socket_queue_head_t * receive_control_queue;
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  int socket_options;
  int socket_send_size;
  int socket_recv_size;
  int listen_queue_len;
  struct sockaddr_in sockaddr;
  unsigned int sockaddr_len;


  if(!sockets)
    return -1;
  if(!parent)
    return -1;
  if(parent->state != MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL)
    return -1;

  sock = malloc(sizeof(struct midnightcode_socket_element_t));
  if(!sock) {
    return -1;
  }
  memset(sock, 0, sizeof(struct midnightcode_socket_element_t));
  receive_chunks = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_chunks) {
    free(sock);
    return -1;
  }
  memset(receive_chunks, 0, sizeof(midnightcode_socket_queue_head_t));
  send_control_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!send_control_queue) {
    free(sock);
    free(receive_chunks);
    return -1;
  }
  memset(send_control_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  send_data_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!send_data_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    return -1;
  }
  memset(send_data_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  receive_control_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_control_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    free(send_data_queue);
    return -1;
  }
  memset(receive_control_queue, 0, sizeof(midnightcode_socket_queue_head_t));
  receive_data_queue = malloc(sizeof(midnightcode_socket_queue_head_t));
  if(!receive_data_queue) {
    free(sock);
    free(receive_chunks);
    free(send_control_queue);
    free(send_data_queue);
    free(receive_control_queue);
    return -1;
  }
  memset(receive_data_queue, 0, sizeof(midnightcode_socket_queue_head_t));


  sock->receive_chunks = receive_chunks;
  sock->send_control_queue = send_control_queue;
  sock->receive_control_queue = receive_control_queue;
  sock->send_data_queue = send_data_queue;
  sock->receive_data_queue = receive_data_queue;

  socket_recv_size = MIDNIGHTCODE_SOCKET_RECEIVE_BLOCK;
  socket_send_size = MIDNIGHTCODE_SOCKET_SEND_BLOCK;
  listen_queue_len = MIDNIGHTCODE_SOCKET_INBOUND_MAX_Q;

  sock->control = parent->control;
  sock->direction = MIDNIGHTCODE_SOCKET_DIR_RECEIVE;
  sock->protocol = parent->protocol;
  sock->state = MIDNIGHTCODE_SOCKET_STATE_INITIALISE;

  sock->dpd_enabled = 0;
  sock->dpd_interval = 0;
  sock->dpd_fails = 0;
  sock->dpd_max_interval = 0;
  sock->dpd_max_fails = 0;

  sock->version = MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN;
  sock->ver_interval = 0;
  sock->ver_requests = 0;
  sock->ver_max_interval = 0;
  sock->ver_max_requests = 0;

  sock->invalid_notifies = 0;
  sock->invalid_blocks = 0;

  sockaddr_len = sizeof(struct sockaddr_in);
  memset(&sockaddr, 0, sizeof(struct sockaddr_in));

  switch(sock->protocol) {
    case MIDNIGHTCODE_SOCKET_PROTOCOL_TCP:
      sock->socket = accept(parent->socket, (struct sockaddr *)&sockaddr,
        &sockaddr_len);
      break;

    case MIDNIGHTCODE_SOCKET_PROTOCOL_UDP:
      /* NOTE: _accept will not carry UDP (no inbound "connection") */
      break;

    default:
      sock->socket = -1;
      break;
  }
  if(sock->socket < 0) {
    midnightcode_socket_closeshut(sockets, sock,
      MIDNIGHTCODE_SOCKET_STATE_DESTROY);
    return -1;
  }

  switch(sock->protocol) {
    case MIDNIGHTCODE_SOCKET_PROTOCOL_TCP:
      if(setsockopt(sock->socket, SOL_SOCKET, SO_SNDBUF,
        (char *)&socket_send_size, (int)sizeof(socket_send_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        return -1;
      }
      if(setsockopt(sock->socket, SOL_SOCKET, SO_RCVBUF,
        (char *)&socket_recv_size, (int)sizeof(socket_recv_size)) < 0) {
        midnightcode_socket_closeshut(sockets, sock,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        return -1;
      }
      break;

    default:
      break;
  }

  socket_options = fcntl(sock->socket, F_GETFL, NULL);
  socket_options |= O_NONBLOCK;
  fcntl(sock->socket, F_SETFL, socket_options);

  memcpy(&sock->ipv4_local, &parent->ipv4_local, sizeof(struct in_addr));
  snprintf(sock->ipv4_local_s, 32, "%s", inet_ntoa(sock->ipv4_local));
  sock->port_local = parent->port_local;

  memset(&sock->ipv4_remote, 0, sizeof(struct in_addr));
  memcpy(&sock->ipv4_remote, &sockaddr.sin_addr, sizeof(struct in_addr));
  snprintf(sock->ipv4_remote_s, 32, "%s", inet_ntoa(sock->ipv4_remote));
  sock->port_remote = ntohs(sockaddr.sin_port);

  switch(sock->control) {
    case MIDNIGHTCODE_SOCKET_CONTROL_RAW:
      /* TODO: Consider any raw actions .. ? */
      break;

    case MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE:
      if(parent->dpd_enabled) {
        sock->dpd_enabled = 1;
        sock->dpd_interval = parent->dpd_max_interval;
        sock->dpd_fails = 0;
        sock->dpd_max_interval = parent->dpd_max_interval;
        sock->dpd_max_fails = parent->dpd_max_fails;
      }
      sock->version = MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN;
      sock->ver_interval = parent->ver_max_interval;
      sock->ver_requests = 0;
      sock->ver_max_interval = parent->ver_max_interval;
      sock->ver_max_requests = parent->ver_max_requests;
      break;

    default:
      close(sock->socket);
      midnightcode_socket_closeshut(sockets, sock,
        MIDNIGHTCODE_SOCKET_STATE_DESTROY);
      return -1;
      break;
  }

  sock->max_connections = 0;
  sock->state = MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL;

  midnightcode_list_append(sockets, sock);

  if(sock->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
    midnightcode_socket_control_dispatch(
      sock, MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP, NULL, 0);
  }


  return 0;
}



int
midnightcode_socket_membrane_size_est(int encode, int block_type, int size) {
  int start_tag_len;
  int end_tag_len;
  int ret_i;


  ret_i = 0;
  switch(encode) {
    case MIDNIGHTCODE_SOCKET_CODED_ENCODE:
      start_tag_len = 0;
      end_tag_len = 0;
      switch(block_type) {
        case MIDNIGHTCODE_SOCKET_PACKET_CONTROL:
          start_tag_len = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START);
          end_tag_len = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START);
          break;

        case MIDNIGHTCODE_SOCKET_PACKET_DATA:
          start_tag_len = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START);
          end_tag_len = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START);
          break;

        default:
          return -1;

      }
      ret_i = (4 * (size + 3) / 3) + start_tag_len + end_tag_len + 1;
      break;

    case MIDNIGHTCODE_SOCKET_CODED_DECODE:
      ret_i = (3 * (size + 4) / 4) + 1;
      break;

    default:
      ret_i = -1;
      break;
  }


  return ret_i;
}



int
midnightcode_socket_membrane(int encode, int block_type,
                            char * dst_buffer, char * src_buffer,
                            int dst_buffer_len, int src_buffer_len) {
  char * start_tag;
  char * end_tag;
  char * base64_buffer;
  char * output_buffer;
  int start_tag_len;
  int end_tag_len;
  int start_tag_offset;
  int end_tag_offset;
  int base64_buffer_len;
  int output_buffer_len;
  int base64_len;


  if(!dst_buffer)
    return -1;
  if(!src_buffer)
    return -1;
  if(dst_buffer_len <= 0 || src_buffer_len <= 0)
    return -1;

  switch(block_type) {
    case MIDNIGHTCODE_SOCKET_PACKET_CONTROL:
      start_tag_len = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START);
      start_tag = malloc(start_tag_len);
      if(!start_tag)
        return -1;
      memset(start_tag, 0, start_tag_len);
      memcpy(start_tag, MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START,
        start_tag_len);
      end_tag_len = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END);
      end_tag = malloc(end_tag_len);
      if(!end_tag) {
        free(start_tag);
        return -1;
      }
      memset(end_tag, 0, end_tag_len);
      memcpy(end_tag, MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END, end_tag_len);
      break;

    case MIDNIGHTCODE_SOCKET_PACKET_DATA:
      start_tag_len = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START);
      start_tag = malloc(start_tag_len);
      if(!start_tag)
        return -1;
      memset(start_tag, 0, start_tag_len);
      memcpy(start_tag, MIDNIGHTCODE_SOCKET_DATA_BLOCK_START,
        start_tag_len);
      end_tag_len = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END);
      end_tag = malloc(end_tag_len);
      if(!end_tag) {
        free(start_tag);
        return -1;
      }
      memset(end_tag, 0, end_tag_len);
      memcpy(end_tag, MIDNIGHTCODE_SOCKET_DATA_BLOCK_END, end_tag_len);
      break;

    default:
      return -1;
      break;

  }

  /* TODO: Consider more detailed return values (ie -2 == get bigger buf) */
  switch(encode) {
    case MIDNIGHTCODE_SOCKET_CODED_ENCODE:
      base64_buffer_len = (4 * (src_buffer_len + 3) / 3) +
        start_tag_len + end_tag_len + 1;
      base64_buffer = malloc(base64_buffer_len);
      if(!base64_buffer) {
        free(start_tag);
        free(end_tag);
        return -1;
      }
      memset(base64_buffer, 0, base64_buffer_len);
      base64_len = midnightcode_base64_encode(
        (unsigned char *)base64_buffer, (unsigned char *)src_buffer,
        base64_buffer_len, src_buffer_len);
      if(base64_len <= 0) {
        free(base64_buffer);
        free(start_tag);
        free(end_tag);
        return -1;
      }
      output_buffer_len = base64_len + start_tag_len + end_tag_len;
      if(output_buffer_len <= 0 ||
        output_buffer_len > dst_buffer_len) {
        free(base64_buffer);
        free(start_tag);
        free(end_tag);
        return -1;
      }
      output_buffer = malloc(output_buffer_len);
      if(!output_buffer) {
        free(base64_buffer);
        free(start_tag);
        free(end_tag);
        return -1;
      }
      memset(output_buffer, 0, output_buffer_len);
      memcpy(output_buffer, start_tag, start_tag_len);
      memcpy(&(output_buffer[start_tag_len]), base64_buffer, base64_len);
      memcpy(&(output_buffer[(start_tag_len + base64_len)]), end_tag,
        end_tag_len);
      free(base64_buffer);
      free(start_tag);
      free(end_tag);
      memcpy(dst_buffer, output_buffer, output_buffer_len);
      free(output_buffer);
      return output_buffer_len;
      break;

    case MIDNIGHTCODE_SOCKET_CODED_DECODE:
      if(src_buffer_len <= (start_tag_len + end_tag_len)) {
        free(start_tag);
        free(end_tag);
        return -1;
      }
      start_tag_offset = 0;
      if(memcmp(&src_buffer[start_tag_offset], start_tag, start_tag_len)) {
        free(start_tag);
        free(end_tag);
        return -1;
      }
      end_tag_offset = src_buffer_len - end_tag_len;
      if(memcmp(&src_buffer[end_tag_offset], end_tag, end_tag_len)) {
        free(start_tag);
        free(end_tag);
        return -1;
      }
      base64_buffer_len = (3 * (src_buffer_len + 4) / 4) + 1;
      base64_buffer = malloc(base64_buffer_len);
      if(!base64_buffer) {
        free(start_tag);
        free(end_tag);
        return -1;
      }
      memset(base64_buffer, 0, base64_buffer_len);
      base64_len = midnightcode_base64_decode(
        (unsigned char *)base64_buffer,
        (unsigned char *)&(src_buffer[(start_tag_offset + start_tag_len)]),
        base64_buffer_len,
        end_tag_offset - start_tag_offset - start_tag_len);
      if(base64_len <= 0) {
        free(base64_buffer);
        free(start_tag);
        free(end_tag);
        return -1;
      }
      free(start_tag);
      free(end_tag);
      memcpy(dst_buffer, base64_buffer, base64_len);
      free(base64_buffer);
      return base64_len;
      break;

    default:
      break;

  }

  free(start_tag);
  free(end_tag);


  return -1;
}



int
midnightcode_socket_control_dispatch(
                            struct midnightcode_socket_element_t * socket,
                            unsigned int command_i,
                            char * buffer, int buffer_size) {
  struct midnightcode_socket_queue_head_t * send_control_queue;
  struct midnightcode_socket_queue_element_t * send_control;
  char * block_buffer;
  char * message_ptr;
  char version_net_s[MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1];
  int block_buffer_size;
  int ret_i;


  if(!socket)
    return -1;
  if(!socket->send_control_queue)
    return -1;
  if(socket->control != MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE)
    return -1;

  ret_i = 0;
  send_control_queue = socket->send_control_queue;

  block_buffer_size = MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN;
  block_buffer = malloc(block_buffer_size);
  if(!block_buffer) {
    return -1;
  }
  *(block_buffer) = command_i;

  switch(command_i) {
    case MIDNIGHTCODE_SOCKET_COMMAND_VERSREQ:
      socket->ver_requests++;
      break;

    case MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP:
      block_buffer_size += MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN;
      block_buffer = realloc(block_buffer, block_buffer_size);
      if(!block_buffer) {
        return -1;
      }
      memset(version_net_s, 0,
        MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1);
      snprintf(version_net_s, MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1,
        "%05.2f", libmidnightcode_version);
      message_ptr = &(block_buffer[MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN]);
      memcpy(message_ptr,
        version_net_s, MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN);
      break;

    case MIDNIGHTCODE_SOCKET_COMMAND_ECHOREQ:
      socket->dpd_fails++;
      block_buffer_size += MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHOREQ_LEN;
      block_buffer = realloc(block_buffer, block_buffer_size);
      if(!block_buffer) {
        return -1;
      }
      message_ptr = &(block_buffer[MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN]);
      memset(message_ptr, 0, MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHOREQ_LEN);
      snprintf(message_ptr, MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN,
        "%s", "** ECHO DATA **");
      break;

    case MIDNIGHTCODE_SOCKET_COMMAND_ECHORSP:
      block_buffer_size += MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN;
      block_buffer = realloc(block_buffer, block_buffer_size);
      if(!block_buffer) {
        return -1;
      }
      message_ptr = &(block_buffer[MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN]);
      memset(message_ptr, 0, MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN);
      /* TODO: Consider validity of sending a null ECHORSP */
      if(buffer != NULL &&
        buffer_size == MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN) {
        memcpy(message_ptr, buffer, buffer_size);
      }
      break;

    case MIDNIGHTCODE_SOCKET_COMMAND_INVALID:
      socket->invalid_blocks++;
      /* TODO: Consider expanding and integrating invalid notifications */
      break;

    default:
      free(block_buffer);
      return -1;
      break;

  }

  send_control = malloc(sizeof(struct midnightcode_socket_queue_element_t));
  if(!send_control) {
    free(block_buffer);
    return -1;
  }
  memset(send_control, 0, sizeof(struct midnightcode_socket_queue_element_t));
  send_control->buffer_size =
    midnightcode_socket_membrane_size_est(MIDNIGHTCODE_SOCKET_CODED_ENCODE,
    MIDNIGHTCODE_SOCKET_PACKET_CONTROL, block_buffer_size);
  if(send_control->buffer_size <= 0) {
    free(block_buffer);
    free(send_control);
    return -1;
  }
  send_control->buffer = malloc(send_control->buffer_size);
  if(!(send_control->buffer)) {
    free(block_buffer);
    free(send_control);
    return -1;
  }
  memset(send_control->buffer, 0, send_control->buffer_size);
  send_control->buffer_size = midnightcode_socket_membrane(
    MIDNIGHTCODE_SOCKET_CODED_ENCODE, MIDNIGHTCODE_SOCKET_PACKET_CONTROL,
    send_control->buffer, block_buffer,
    send_control->buffer_size, block_buffer_size);
  free(block_buffer); 
  if(send_control->buffer_size <= 0) {
    free(send_control->buffer);
    free(send_control);
    return -1;
  }
  send_control->buffer = realloc(send_control->buffer,
    send_control->buffer_size);
  if(!(send_control->buffer)) {
    free(send_control);
    return -1;
  }

  midnightcode_list_append(send_control_queue, send_control);
  ret_i++;


  return ret_i;
}



int
midnightcode_socket_control_processor(
                            struct midnightcode_socket_head_t * sockets,
                            struct midnightcode_socket_element_t * socket) {
  struct midnightcode_socket_queue_head_t * receive_control_queue;
  struct midnightcode_socket_queue_element_t * receive_control;
  struct midnightcode_socket_queue_element_t * old_control;
  unsigned int command_i;
  float version_f;
  char version_net_s[MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1];
  char version_src_s[MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1];
  char * message_ptr;
  char * block_buffer;
  int block_buffer_size;
  int ret_i;


  if(!sockets)
    return -1;
  if(!socket)
    return -1;
  if(socket->control != MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE)
    return -1;
  if(!socket->receive_control_queue)
    return -1;

  receive_control_queue = socket->receive_control_queue;
  if(midnightcode_list_count(receive_control_queue) == 0)
    return 0;

  ret_i = 0;
  midnightcode_list_for_each(receive_control, receive_control_queue) {
    if(receive_control->buffer == NULL ||
      receive_control->buffer_size < MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN) {
      old_control = receive_control;
      midnightcode_list_remove(receive_control_queue, receive_control);
      if(old_control != NULL) {
        if(old_control->buffer != NULL)
          free(old_control->buffer);
        free(old_control);
      }
      continue;
    }

    block_buffer_size =
      midnightcode_socket_membrane_size_est(MIDNIGHTCODE_SOCKET_CODED_DECODE,
      MIDNIGHTCODE_SOCKET_PACKET_CONTROL, receive_control->buffer_size);
    if(block_buffer_size <= 0) {
      old_control = receive_control;
      midnightcode_list_remove(receive_control_queue, receive_control);
      if(old_control != NULL) {
        if(old_control->buffer != NULL)
          free(old_control->buffer);
        free(old_control);
      }
      continue;
    }
    block_buffer = malloc(block_buffer_size);
    if(!(block_buffer)) {
      old_control = receive_control;
      midnightcode_list_remove(receive_control_queue, receive_control);
      if(old_control != NULL) {
        if(old_control->buffer != NULL)
          free(old_control->buffer);
        free(old_control);
      }
      continue;
    }
    memset(block_buffer, 0, block_buffer_size);
    block_buffer_size = midnightcode_socket_membrane(
      MIDNIGHTCODE_SOCKET_CODED_DECODE, MIDNIGHTCODE_SOCKET_PACKET_CONTROL,
      block_buffer, receive_control->buffer,
      block_buffer_size, receive_control->buffer_size);
    old_control = receive_control;
    midnightcode_list_remove(receive_control_queue, receive_control);
    if(old_control != NULL) {
      if(old_control->buffer != NULL)
        free(old_control->buffer);
      free(old_control);
    }
    if(block_buffer_size <= 0) {
      free(block_buffer);
      continue;
    }

    command_i = (unsigned int)*(block_buffer);
    
    switch(command_i) {
      case MIDNIGHTCODE_SOCKET_COMMAND_VERSREQ:
        if(block_buffer_size ==
          (MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN +
          MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSREQ_LEN)) {
          midnightcode_socket_control_dispatch(socket,
            MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP, NULL, 0);
            ret_i++;
        }
        break;

      case MIDNIGHTCODE_SOCKET_COMMAND_VERSRSP:
        socket->version = MIDNIGHTCODE_SOCKET_VERSION_ERROR;
        if(block_buffer_size ==
          (MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN +
          MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN)) {
          memset(version_net_s, 0,
            MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1);
          memcpy(&version_net_s,
            &(block_buffer[MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN]),
            MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN);
          if(midnightcode_parse_strtovar(&version_f, version_net_s, 0, 0,
            MIDNIGHTCODE_PARSE_TYPE_FLOAT) == 0) {
            memset(version_net_s, 0,
              MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1);
            memset(version_src_s, 0,
              MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1);
            snprintf(version_net_s,
              MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1,
              "%05.2f", version_f);
            snprintf(version_src_s,
              MIDNIGHTCODE_SOCKET_CONTROL_CMD_VERSRSP_LEN + 1,
              "%05.2f", libmidnightcode_version);
            if(strncmp(version_src_s, version_net_s,
              strlen(version_src_s)) == 0) {
                socket->version = MIDNIGHTCODE_SOCKET_VERSION_EQUAL;
                ret_i++;
            }
          }
        }
        if(socket->version == MIDNIGHTCODE_SOCKET_VERSION_ERROR) {
          midnightcode_socket_closeshut(sockets, socket,
            MIDNIGHTCODE_SOCKET_STATE_CLOSE);
        }
        break;

      case MIDNIGHTCODE_SOCKET_COMMAND_ECHOREQ:
        if(block_buffer_size ==
          (MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN +
          MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHOREQ_LEN)) {
          message_ptr = &(block_buffer[MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN]);
          /* TODO: Consider failed responses as acceptable? Error check? */
          midnightcode_socket_control_dispatch(socket,
            MIDNIGHTCODE_SOCKET_COMMAND_ECHORSP,
            message_ptr, MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHOREQ_LEN);
            ret_i++;
        }
        break;

      case MIDNIGHTCODE_SOCKET_COMMAND_ECHORSP:
        if(block_buffer_size ==
          (MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN +
          MIDNIGHTCODE_SOCKET_CONTROL_CMD_ECHORSP_LEN)) {
          if(socket->dpd_fails > 0) {
            socket->dpd_fails -= 1;
          }
          ret_i++;
        }
        break;

      case MIDNIGHTCODE_SOCKET_COMMAND_INVALID:
        if(block_buffer_size ==
          MIDNIGHTCODE_SOCKET_CONTROL_CMD_LEN) {
          socket->invalid_notifies++;
          /* TODO: Consider integration of invalid block notification */
          ret_i++;
        }
        break;

      default:
        socket->version = MIDNIGHTCODE_SOCKET_VERSION_ERROR;
        break;

    }

    free(block_buffer);

  }


  return ret_i;
}



int
midnightcode_socket_receive_extract_bit(
                    struct midnightcode_socket_queue_head_t * receive_chunks,
                    struct midnightcode_socket_queue_element_t * receive_chunk,
                    char ** destination_buf, char ** destination_ptr,
                    int * destination_len,
                    int chunk_count,
                    struct midnightcode_socket_tag_t * tag,
                    int store, int delete, int * block_exists) {
  struct midnightcode_socket_queue_element_t * old_chunk;
  char * source_ptr;
  int source_len;
  int store_bit;
  int delete_bit;
  int next_step;


  delete_bit = 0;
  store_bit = 0;

  next_step = 0;

  source_ptr = NULL;
  source_len = 0;

  if(!*block_exists)
    return 0;

  if(chunk_count < tag->open_tag_chunk) {
    source_ptr = receive_chunk->buffer;
    source_len = receive_chunk->buffer_size;
    delete_bit = 1;
  } else if(chunk_count == tag->open_tag_chunk) {
    source_ptr = receive_chunk->buffer + tag->open_tag_offset;
    if(tag->open_tag_chunk == tag->close_tag_chunk) {
      source_len = tag->close_tag_offset - tag->open_tag_offset + 1;
      store_bit = 1;
      delete_bit = 1;
      next_step = 1;
    } else {
      source_len = receive_chunk->buffer_size - tag->open_tag_offset;
      store_bit = 1;
      delete_bit = 1;
    }
  } else if(chunk_count > tag->open_tag_chunk &&
    chunk_count < tag->close_tag_chunk) {
    source_ptr = receive_chunk->buffer;
    source_len = receive_chunk->buffer_size;
    store_bit = 1;
    delete_bit = 1;
  } else if(chunk_count == tag->close_tag_chunk) {
    source_ptr = receive_chunk->buffer;
    source_len = tag->close_tag_offset + 1;
    store_bit = 1;
    delete_bit = 1;
    next_step = 1;
  }

  if(source_ptr != NULL && source_len > 0) {
    if(store && store_bit) {
      *destination_len += source_len;
      *destination_buf = realloc(*destination_buf, *destination_len);
      if(*destination_buf == NULL) {
        return -1;
      }
      if(*destination_ptr) {
        *destination_ptr = *destination_buf;
        *destination_ptr += *destination_len - source_len;
      } else {
        *destination_ptr = *destination_buf;
      }
      memset(*destination_ptr, 0, source_len);
      memcpy(*destination_ptr, source_ptr, source_len);
      *destination_ptr += source_len;
    }
    if(delete && delete_bit) {
      if((source_ptr + source_len) ==
        (receive_chunk->buffer + receive_chunk->buffer_size)) {
        old_chunk = receive_chunk;
        midnightcode_list_remove(receive_chunks, receive_chunk);
        if(old_chunk != NULL) {
          if(old_chunk->buffer != NULL)
            free(old_chunk->buffer);
          free(old_chunk);
        }
        *block_exists = 0;
      } else {
        memset(source_ptr, 0, source_len);
      }
    }
  }


  return next_step;
}


int
midnightcode_socket_receive_extract(
                            struct midnightcode_socket_head_t * sockets,
                            struct midnightcode_socket_element_t * socket,
                            struct midnightcode_socket_tag_t * open_tag,
                            struct midnightcode_socket_tag_t * close_tag,
                            int enclosed_by, int store_tags, int block_type,
                            char ** buffer, int * buffer_len) {
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_element_t * receive_chunk;
  struct midnightcode_socket_tag_t * data_tag;
  char * destination_ptr;
  char * destination_buf;
  char * terminate_tag;
  int destination_len;
  int terminate_tag_len;
  int chunk_count;
  int extract_step;
  int block_exists;
  int store_bit;
  int delete_bit;
  int eb_ret_i;
  int ret_i;


  if(!sockets)
    return -1;
  if(!socket)
    return -1;
  if(!socket->receive_chunks)
    return -1;

  receive_chunks = socket->receive_chunks;

  if(midnightcode_list_count(receive_chunks) == 0)
    return 0;

  if(open_tag->open_tag_chunk < 0 || open_tag->open_tag_offset < 0)
    return -1;
  if(open_tag->close_tag_chunk < 0 || open_tag->close_tag_offset < 0)
    return -1;
  if(close_tag->open_tag_chunk < 0 || close_tag->open_tag_offset < 0)
    return -1;
  if(close_tag->close_tag_chunk < 0 || close_tag->close_tag_offset < 0)
    return -1;
  if(store_tags != MIDNIGHTCODE_SOCKET_TAGGED_NATURAL &&
    store_tags != MIDNIGHTCODE_SOCKET_TAGGED_FULL)
    return -1;
  if(block_type != MIDNIGHTCODE_SOCKET_PACKET_DATA &&
    block_type != MIDNIGHTCODE_SOCKET_PACKET_CONTROL)
    return -1;

  terminate_tag = NULL;
  terminate_tag_len = 0;
  if(enclosed_by != MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL &&
    store_tags == MIDNIGHTCODE_SOCKET_TAGGED_FULL) {
    if(block_type == MIDNIGHTCODE_SOCKET_PACKET_CONTROL) {
      terminate_tag_len = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END);
      terminate_tag = malloc(terminate_tag_len);
      if(!terminate_tag)
        return -1;
      memset(terminate_tag, 0, terminate_tag_len);
      memcpy(terminate_tag, MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END,
        terminate_tag_len);
    }
    if(block_type == MIDNIGHTCODE_SOCKET_PACKET_DATA) {
      terminate_tag_len = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END);
      terminate_tag = malloc(terminate_tag_len);
      if(!terminate_tag)
        return -1;
      memset(terminate_tag, 0, terminate_tag_len);
      memcpy(terminate_tag, MIDNIGHTCODE_SOCKET_DATA_BLOCK_END,
        terminate_tag_len);
    }
  }

  data_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!data_tag) {
    if(terminate_tag)
      free(terminate_tag);
    return -1;
  }
  memset(data_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  data_tag->open_tag_chunk = open_tag->close_tag_chunk; 
  data_tag->open_tag_offset = open_tag->close_tag_offset + 1; 
  data_tag->close_tag_chunk = close_tag->open_tag_chunk; 
  data_tag->close_tag_offset = close_tag->open_tag_offset - 1;

  destination_buf = NULL;
  destination_ptr = destination_buf;
  destination_len = 0;
  chunk_count = 0;
  extract_step = MIDNIGHTCODE_SOCKET_EXTRACT_START_TAG;
  ret_i = 0;

  midnightcode_list_for_each(receive_chunk, receive_chunks) {

    block_exists = 1;
    switch(extract_step) {

      case MIDNIGHTCODE_SOCKET_EXTRACT_COMPLETE:
        break;

      case MIDNIGHTCODE_SOCKET_EXTRACT_START_TAG:
        eb_ret_i = 0;
        store_bit = 0;
        delete_bit = 1;
        if(store_tags == MIDNIGHTCODE_SOCKET_TAGGED_FULL)
          store_bit = 1;
        eb_ret_i = midnightcode_socket_receive_extract_bit(
          receive_chunks, receive_chunk, 
          &destination_buf, &destination_ptr, &destination_len,
          chunk_count, open_tag, store_bit, delete_bit,
          &block_exists);
        if(eb_ret_i > 0) {
          extract_step = MIDNIGHTCODE_SOCKET_EXTRACT_DATA;
          ret_i |= MIDNIGHTCODE_SOCKET_EXTRACT_START_TAG;
        } else if(eb_ret_i < 0) {
          if(terminate_tag)
            free(terminate_tag);
          free(data_tag);
          return -1;
        }
        if(!block_exists)
          break;

      case MIDNIGHTCODE_SOCKET_EXTRACT_DATA:
        eb_ret_i = 0;
        store_bit = 1;
        delete_bit = 1;
        eb_ret_i = midnightcode_socket_receive_extract_bit(
          receive_chunks, receive_chunk, 
          &destination_buf, &destination_ptr, &destination_len,
          chunk_count, data_tag, store_bit, delete_bit,
          &block_exists);
        if(eb_ret_i > 0) {
          extract_step = MIDNIGHTCODE_SOCKET_EXTRACT_END_TAG;
          ret_i |= MIDNIGHTCODE_SOCKET_EXTRACT_DATA;
        } else if(eb_ret_i < 0) {
          if(terminate_tag)
            free(terminate_tag);
          free(data_tag);
          return -1;
        }
        if(!block_exists)
          break;

      case MIDNIGHTCODE_SOCKET_EXTRACT_END_TAG:
        eb_ret_i = 0;
        store_bit = 0;
        delete_bit = 1;
        if(enclosed_by != MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL) {
          delete_bit = 0;
        } else {
          if(store_tags == MIDNIGHTCODE_SOCKET_TAGGED_FULL)
            store_bit = 1;
        }
        eb_ret_i = midnightcode_socket_receive_extract_bit(
          receive_chunks, receive_chunk, 
          &destination_buf, &destination_ptr, &destination_len,
          chunk_count, close_tag, store_bit, delete_bit,
          &block_exists);
        if(eb_ret_i > 0) {
          extract_step = MIDNIGHTCODE_SOCKET_EXTRACT_COMPLETE;
          ret_i |= MIDNIGHTCODE_SOCKET_EXTRACT_END_TAG;
          if(store_tags == MIDNIGHTCODE_SOCKET_TAGGED_FULL &&
            enclosed_by != MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL) {
            destination_len += terminate_tag_len;
            destination_buf = realloc(destination_buf, destination_len);
            if(destination_buf == NULL) {
              if(terminate_tag)
                free(terminate_tag);
              free(data_tag);
              return -1;
            }
            if(destination_ptr) {
              destination_ptr = destination_buf;
              destination_ptr += destination_len - terminate_tag_len;
            } else {
              destination_ptr = destination_buf;
            }
            memset(destination_ptr, 0, terminate_tag_len);
            memcpy(destination_ptr, terminate_tag, terminate_tag_len);
            destination_ptr += terminate_tag_len;
          }
        } else if(eb_ret_i < 0) {
          if(terminate_tag)
            free(terminate_tag);
          free(data_tag);
          return -1;
        }
        if(!block_exists)
          break;

    }

    if(extract_step == MIDNIGHTCODE_SOCKET_EXTRACT_COMPLETE) {
      break;
    }

    chunk_count++;
  }
  if(terminate_tag)
    free(terminate_tag);
  free(data_tag);

  *buffer_len = destination_len;
  *buffer = destination_buf;


  return ret_i;
}



int
midnightcode_socket_receive_seek(
                            struct midnightcode_socket_head_t * sockets,
                            struct midnightcode_socket_element_t * socket,
                            char * key, int key_len,
                            int least_start_chunk, int least_start_offset,
                            int max_start_chunk, int max_start_offset,
                            struct midnightcode_socket_tag_t ** tag) {
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_element_t * receive_chunk;
  struct midnightcode_socket_tag_t * ltag;
  char * needle;
  char * haystack_ptr;
  int needle_len;
  int needle_offset;
  int haystack_offset;
  int chunk_count;
  int octet_count;
  unsigned int octet_overrun;


  if(!sockets)
    return -1;
  if(!socket)
    return -1;
  if(!socket->receive_chunks)
    return -1;

  receive_chunks = socket->receive_chunks;

  if(midnightcode_list_count(receive_chunks) == 0)
    return 0;

  if(key_len <= 0)
    return -1;
  if(key_len >= MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX)
    return -1;
  if(least_start_chunk < -1)
    return -1;
  if(least_start_offset < -1)
    return -1;
  if(max_start_chunk < -1)
    return -1;
  if(max_start_offset < -1)
    return -1;

  if(least_start_chunk == -1 || least_start_offset == -1) {
    least_start_chunk = -1;
    least_start_offset = -1;
  }
  if(max_start_chunk == -1 || max_start_offset == -1) {
    max_start_chunk = -1;
    max_start_offset = -1;
  }

  needle_len = key_len;
  needle = malloc(needle_len);
  if(!needle)
    return -1;
  memset(needle, 0, needle_len);
  memcpy(needle, key, needle_len);
  needle_offset = 0;

  octet_overrun = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START);
  if(octet_overrun < strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END))
    octet_overrun = strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END);
  if(octet_overrun < strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START))
    octet_overrun = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START);
  if(octet_overrun < strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END))
    octet_overrun = strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END);

  ltag = *tag;
  ltag->open_tag_chunk = 0;
  ltag->open_tag_offset = 0;
  ltag->close_tag_chunk = 0;
  ltag->close_tag_offset = 0;

  chunk_count = -1;
  octet_count = 0;
  midnightcode_list_for_each(receive_chunk, receive_chunks) {
    chunk_count++;

    /* TODO: Detect empty WIRE_BLOCK_MAX extent and delete */

    if(max_start_chunk != -1) {
      if(chunk_count > max_start_chunk)
        break;
    }
    if(least_start_chunk != -1) {
      if(chunk_count < least_start_chunk) {
        continue;
      }
    }

    haystack_ptr = receive_chunk->buffer;

    for(haystack_offset = 0;
      haystack_offset < receive_chunk->buffer_size;
      haystack_offset++) {

      if(max_start_chunk != -1) {
        if(chunk_count == max_start_chunk) {
          if(haystack_offset >= max_start_offset)
            break;
        }
      }
      if(least_start_chunk != -1) {
        if(chunk_count == least_start_chunk) {
          if(haystack_offset < least_start_offset) {
            continue;
          }
        }
      }

      if(*(haystack_ptr + haystack_offset) == *(needle + needle_offset)) {

        if(needle_offset == 0) {
          ltag->open_tag_chunk = chunk_count;
          ltag->open_tag_offset = haystack_offset;
        }

        if((needle_offset + 1) == needle_len) {
          ltag->close_tag_chunk = chunk_count;
          ltag->close_tag_offset = haystack_offset;
          free(needle);
          return 1;
        } else {
          needle_offset++;
        }

      } else {
        needle_offset = 0;
      }
      octet_count++;
    }
  }

  free(needle);
  ltag->open_tag_chunk = 0;
  ltag->open_tag_offset = 0;

  /* TODO: Detect empty WIRE_BLOCK_MAX extent and delete */
  /*
  if(octet_count >= MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX) {
    return -1;
  }
  */

  ltag->close_tag_chunk = 0;
  ltag->close_tag_offset = 0;


  return 0;
}



int
midnightcode_socket_receive_chunk(
                            struct midnightcode_socket_head_t * sockets,
                            struct midnightcode_socket_element_t * socket,
                            int enclosure_option, int include_tags,
                            char ** buffer, int * buffer_len) {
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_tag_t * open_store_tag;
  struct midnightcode_socket_tag_t * open_data_tag;
  struct midnightcode_socket_tag_t * open_control_tag;
  struct midnightcode_socket_tag_t * close_store_tag;
  struct midnightcode_socket_tag_t * close_data_tag;
  struct midnightcode_socket_tag_t * close_control_tag;
  int least_chunk;
  int least_offset;
  int max_chunk;
  int max_offset;
  int enclosed_by;
  int block_type;
  int ret_i;


  if(!sockets)
    return -1;
  if(!socket)
    return -1;
  if(!socket->receive_chunks)
    return -1;
  if(enclosure_option == MIDNIGHTCODE_SOCKET_ENCLOSURE_UNKNOWN)
     enclosure_option = MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY;
  if(enclosure_option != MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL &&
     enclosure_option != MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY)
    return -1;
  if(include_tags != MIDNIGHTCODE_SOCKET_TAGGED_NATURAL &&
    include_tags != MIDNIGHTCODE_SOCKET_TAGGED_FULL)
    return -1;

  receive_chunks = socket->receive_chunks;

  ret_i = MIDNIGHTCODE_SOCKET_PACKET_NONE;

  if(midnightcode_list_count(receive_chunks) == 0) {
    return ret_i;
  }

  open_store_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!open_store_tag) {
    return -1;
  }
  open_data_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!open_data_tag) {
    free(open_store_tag);
    return -1;
  }
  open_control_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!open_control_tag) {
    free(open_store_tag);
    free(open_data_tag);
    return -1;
  }
  close_store_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!close_store_tag) {
    free(open_store_tag);
    free(open_data_tag);
    free(open_control_tag);
    return -1;
  }
  close_data_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!close_data_tag) {
    free(open_store_tag);
    free(open_data_tag);
    free(open_control_tag);
    free(close_store_tag);
    return -1;
  }
  close_control_tag = malloc(sizeof(struct midnightcode_socket_tag_t));
  if(!close_control_tag) {
    free(open_store_tag);
    free(open_data_tag);
    free(open_control_tag);
    free(close_store_tag);
    free(close_data_tag);
    return -1;
  }

  memset(open_store_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(open_data_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(open_control_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(close_store_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(close_data_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(close_control_tag, 0, sizeof(struct midnightcode_socket_tag_t));

  block_type = MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN;
  least_chunk = -1;
  least_offset = -1;
  max_chunk = -1;
  max_offset = -1;
  if(midnightcode_socket_receive_seek(sockets, socket,
        MIDNIGHTCODE_SOCKET_DATA_BLOCK_START,
        strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START),
        least_chunk, least_offset, max_chunk, max_offset,
        &open_data_tag) > 0) {
    block_type = MIDNIGHTCODE_SOCKET_PACKET_DATA;
    memset(open_store_tag, 0, sizeof(struct midnightcode_socket_tag_t));
    memcpy(open_store_tag, open_data_tag,
      sizeof(struct midnightcode_socket_tag_t));
    max_chunk = open_store_tag->open_tag_chunk;
    max_offset = open_store_tag->open_tag_offset;
  }
  if(midnightcode_socket_receive_seek(sockets, socket,
        MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START,
        strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START),
        least_chunk, least_offset, max_chunk, max_offset,
        &open_control_tag) > 0) {
    block_type = MIDNIGHTCODE_SOCKET_PACKET_CONTROL;
    memset(open_store_tag, 0, sizeof(struct midnightcode_socket_tag_t));
    memcpy(open_store_tag, open_control_tag,
      sizeof(struct midnightcode_socket_tag_t));
    max_chunk = open_store_tag->open_tag_chunk;
    max_offset = open_store_tag->open_tag_offset;
  }
  if(block_type == MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN) {
    /* TODO: Detect empty WIRE_BLOCK_MAX extent and delete */
    free(open_store_tag);
    free(open_data_tag);
    free(open_control_tag);
    free(close_store_tag);
    free(close_data_tag);
    free(close_control_tag);
    return ret_i;
  }

  enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_UNKNOWN;
  memset(open_data_tag, 0, sizeof(struct midnightcode_socket_tag_t));
  memset(open_control_tag, 0, sizeof(struct midnightcode_socket_tag_t));

  switch(enclosure_option) {

    case MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL:
      switch(block_type) {

        case MIDNIGHTCODE_SOCKET_PACKET_DATA:
          block_type = MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN;
          least_chunk = open_store_tag->close_tag_chunk;
          least_offset = open_store_tag->close_tag_offset + 1;
          max_chunk = -1;
          max_offset = -1;
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_DATA_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_data_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_data_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL;
            block_type = MIDNIGHTCODE_SOCKET_PACKET_DATA;
          }
          break;

        case MIDNIGHTCODE_SOCKET_PACKET_CONTROL:
          block_type = MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN;
          least_chunk = open_store_tag->close_tag_chunk;
          least_offset = open_store_tag->close_tag_offset + 1;
          max_chunk = -1;
          max_offset = -1;
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_control_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_control_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL;
            block_type = MIDNIGHTCODE_SOCKET_PACKET_CONTROL;
          }
          break;

      }
      break;

    case MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY:
      switch(block_type) {

        case MIDNIGHTCODE_SOCKET_PACKET_DATA:
          least_chunk = open_store_tag->close_tag_chunk;
          least_offset = open_store_tag->close_tag_offset + 1;
          max_chunk = -1;
          max_offset = -1;
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_DATA_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_data_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_data_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL;
            max_chunk = close_store_tag->open_tag_chunk;
            max_offset = close_store_tag->open_tag_offset;
          }
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_control_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_control_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY;
            max_chunk = close_store_tag->open_tag_chunk;
            max_offset = close_store_tag->open_tag_offset;
          }
          break;

        case MIDNIGHTCODE_SOCKET_PACKET_CONTROL:
          least_chunk = open_store_tag->close_tag_chunk;
          least_offset = open_store_tag->close_tag_offset + 1;
          max_chunk = -1;
          max_offset = -1;
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_control_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_control_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_NATURAL;
            max_chunk = close_store_tag->open_tag_chunk;
            max_offset = close_store_tag->open_tag_offset;
          }
          if(midnightcode_socket_receive_seek(sockets, socket,
                MIDNIGHTCODE_SOCKET_DATA_BLOCK_END,
                strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_END),
                least_chunk, least_offset, max_chunk, max_offset,
                &close_data_tag) > 0) {
            memset(close_store_tag, 0,
              sizeof(struct midnightcode_socket_tag_t));
            memcpy(close_store_tag, close_data_tag,
              sizeof(struct midnightcode_socket_tag_t));
            enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY;
            max_chunk = close_store_tag->open_tag_chunk;
            max_offset = close_store_tag->open_tag_offset;
          }
          break;

      }
      if(midnightcode_socket_receive_seek(sockets, socket,
            MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START,
            strlen(MIDNIGHTCODE_SOCKET_CONTROL_BLOCK_START),
            least_chunk, least_offset, max_chunk, max_offset,
            &open_control_tag) > 0) {
        memset(close_store_tag, 0,
          sizeof(struct midnightcode_socket_tag_t));
        memcpy(close_store_tag, open_control_tag,
          sizeof(struct midnightcode_socket_tag_t));
        enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY;
        max_chunk = close_store_tag->open_tag_chunk;
        max_offset = close_store_tag->open_tag_offset;
      }
      if(midnightcode_socket_receive_seek(sockets, socket,
            MIDNIGHTCODE_SOCKET_DATA_BLOCK_START,
            strlen(MIDNIGHTCODE_SOCKET_DATA_BLOCK_START),
            least_chunk, least_offset, max_chunk, max_offset,
            &open_data_tag) > 0) {
        memset(close_store_tag, 0,
          sizeof(struct midnightcode_socket_tag_t));
        memcpy(close_store_tag, open_data_tag,
          sizeof(struct midnightcode_socket_tag_t));
        enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY;
        max_chunk = close_store_tag->open_tag_chunk;
        max_offset = close_store_tag->open_tag_offset;
      }
      if(max_chunk == -1 || max_offset == -1) {
        block_type = MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN;
        enclosed_by = MIDNIGHTCODE_SOCKET_ENCLOSURE_UNKNOWN;
      }
      break;

  }

  if(block_type == MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN) {
    free(open_store_tag);
    free(open_data_tag);
    free(open_control_tag);
    free(close_store_tag);
    free(close_data_tag);
    free(close_control_tag);
    return ret_i;
  }

  // block runs
  //   open_store_tag->open_tag_chunk,open_tag_offset
  //   close_store_tag->close_tag_chunk,close_tag_offset

  free(open_data_tag);
  free(open_control_tag);
  free(close_data_tag);
  free(close_control_tag);

  ret_i = midnightcode_socket_receive_extract(sockets, socket,
    open_store_tag, close_store_tag, enclosed_by, include_tags,
    block_type, buffer, buffer_len);
  if(ret_i > 0) {
    if(include_tags == MIDNIGHTCODE_SOCKET_TAGGED_FULL) {
      if((ret_i & MIDNIGHTCODE_SOCKET_EXTRACT_START_TAG) &&
        (ret_i & MIDNIGHTCODE_SOCKET_EXTRACT_DATA) &&
        (ret_i & MIDNIGHTCODE_SOCKET_EXTRACT_END_TAG)) {
        ret_i = block_type;
      } else {
        ret_i = MIDNIGHTCODE_SOCKET_PACKET_NONE;
      }
    } else {
      if(ret_i & MIDNIGHTCODE_SOCKET_EXTRACT_DATA) {
        ret_i = block_type;
      } else {
        ret_i = MIDNIGHTCODE_SOCKET_PACKET_NONE;
      }
    }
  } else {
    ret_i = MIDNIGHTCODE_SOCKET_PACKET_NONE;
  }

  if(ret_i == MIDNIGHTCODE_SOCKET_PACKET_NONE) {
    if(*buffer) {
      free(*buffer);
      *buffer_len = 0;
    }
  }

  free(open_store_tag);
  free(close_store_tag);
 
  return ret_i;
}



int
midnightcode_socket_receive_processor(
                            struct midnightcode_socket_head_t * sockets,
                            struct midnightcode_socket_element_t * socket) {
  struct midnightcode_socket_queue_head_t * receive_chunks;
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  struct midnightcode_socket_queue_head_t * receive_control_queue;
  struct midnightcode_socket_queue_element_t * new_block;
  int block_type;
  char * block_buffer;
  int block_size;
  int ret_i;


  if(!sockets)
    return -1;
  if(!socket)
    return -1;
  if(!socket->receive_chunks)
    return -1;
  if(socket->control != MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE)
    return -1;
  if(!socket->receive_data_queue)
    return -1;
  if(!socket->receive_control_queue)
    return -1;

  receive_chunks = socket->receive_chunks;
  receive_data_queue = socket->receive_data_queue;
  receive_control_queue = socket->receive_control_queue;

  if(midnightcode_list_count(receive_chunks) == 0)
    return 0;

  ret_i = 0;
  block_type = MIDNIGHTCODE_SOCKET_PACKET_UNKNOWN;
  while(block_type != MIDNIGHTCODE_SOCKET_PACKET_NONE) {

    block_buffer = NULL;
    block_size = 0;

    block_type = midnightcode_socket_receive_chunk(sockets, socket,
      MIDNIGHTCODE_SOCKET_ENCLOSURE_ANY, MIDNIGHTCODE_SOCKET_TAGGED_FULL,
      &block_buffer, &block_size);

    if(block_type != MIDNIGHTCODE_SOCKET_PACKET_DATA &&
      block_type != MIDNIGHTCODE_SOCKET_PACKET_CONTROL) {
      if(block_buffer) {
        free(block_buffer);
      } 
      continue;
    }
    if(block_size <= 0) {
      if(block_buffer) {
        free(block_buffer);
      } 
      break;
    }

    new_block = malloc(sizeof(struct midnightcode_socket_queue_element_t));
    if(!new_block) {
      free(block_buffer);
      ret_i = -1;
      break;
    }
    memset(new_block, 0, sizeof(struct midnightcode_socket_queue_element_t));
    new_block->buffer_size = block_size;
    new_block->buffer = block_buffer;
    if(!(new_block->buffer)) {
      free(new_block);
      free(block_buffer); 
      ret_i = -1;
      break;
    }

    switch(block_type) {

      case MIDNIGHTCODE_SOCKET_PACKET_DATA:
        midnightcode_list_append(receive_data_queue, new_block);
        ret_i++;
        break;

      case MIDNIGHTCODE_SOCKET_PACKET_CONTROL:
        midnightcode_list_append(receive_control_queue, new_block);
        ret_i++;
        break;

      default:
        break;

    }
  }

  return ret_i;
}



void *
midnightcode_socket_handler(void *arg) {
  struct midnightcode_socket_head_t * sockets;
  struct midnightcode_socket_element_t * socket;
  struct midnightcode_socket_queue_head_t * send_control_queue;
  struct midnightcode_socket_queue_element_t * send_control;
  struct midnightcode_socket_queue_element_t * old_send_control;
  struct midnightcode_socket_queue_head_t * send_data_queue;
  struct midnightcode_socket_queue_element_t * send_data;
  struct midnightcode_socket_queue_element_t * old_send_data;
  struct midnightcode_socket_queue_head_t * receive_data_queue;
  struct midnightcode_socket_queue_element_t * receive_data;
  fd_set select_recv_fd;
  fd_set select_send_fd;
  struct timeval select_tv;
  char * receive_buffer;
  int sockaddr_len;
  int receive_buffer_size;
  int receive_octets;
  int send_octets;
  int ret_i;


  arg = NULL;
  send_octets = 0;
  sockaddr_len = sizeof(struct sockaddr);
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

  while(1) {

    pthread_mutex_lock(&midnightcode_socket_mutex_t);

    if(!midnightcode_socket_sockets) {
      pthread_mutex_unlock(&midnightcode_socket_mutex_t);
      pthread_exit(NULL);
    }

    sockets = midnightcode_socket_sockets;

    midnightcode_list_for_each(socket, sockets) {

      if(socket->state == MIDNIGHTCODE_SOCKET_STATE_USER_SHUT) {
        midnightcode_socket_closeshut(sockets, socket,
          MIDNIGHTCODE_SOCKET_STATE_DESTROY);
        continue;
      }
      if(socket->state >= MIDNIGHTCODE_SOCKET_STATE_CLOSE)
        continue;
      if(socket->state != MIDNIGHTCODE_SOCKET_STATE_OPERATIONAL)
        continue;

      /* TODO: Consider failed dispatches as acceptable? Error check? */
      if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE &&
        socket->direction != MIDNIGHTCODE_SOCKET_DIR_LISTEN) {

        /* TODO: Consider this integration of DPD, carefully */
        if(socket->dpd_enabled) {
          if(socket->dpd_fails >= socket->dpd_max_fails) {
            midnightcode_socket_closeshut(sockets, socket,
              MIDNIGHTCODE_SOCKET_STATE_CLOSE);
          }
          if(socket->dpd_interval > 0) {
            socket->dpd_interval--;
          } else {
            socket->dpd_interval = socket->dpd_max_interval;
            midnightcode_socket_control_dispatch(
              socket, MIDNIGHTCODE_SOCKET_COMMAND_ECHOREQ, NULL, 0);
          }
        }

        if(socket->version >= MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN &&
          socket->ver_requests >= socket->ver_max_requests) {
          midnightcode_socket_closeshut(sockets, socket,
            MIDNIGHTCODE_SOCKET_STATE_CLOSE);
        }
        if(socket->version == MIDNIGHTCODE_SOCKET_VERSION_UNKNOWN) {
          if(socket->ver_interval > 0) {
            socket->ver_interval--;
          } else {
            socket->ver_interval = socket->ver_max_interval;
            midnightcode_socket_control_dispatch(
              socket, MIDNIGHTCODE_SOCKET_COMMAND_VERSREQ, NULL, 0);
          }
        }
      }

      FD_ZERO(&select_recv_fd);
      FD_ZERO(&select_send_fd);
      FD_SET(socket->socket, &select_recv_fd);
      FD_SET(socket->socket, &select_send_fd);
      select_tv.tv_sec = 0;
      select_tv.tv_usec = 0;

      ret_i = select(socket->socket + 1, &select_recv_fd, &select_send_fd,
        NULL, &select_tv);

      if(ret_i == 0)
        continue;

      if(ret_i < 0) {
        if(errno == EBADF || errno == EINVAL || errno == ENOMEM) {
          midnightcode_socket_closeshut(sockets, socket,
            MIDNIGHTCODE_SOCKET_STATE_CLOSE);
        } else {
          continue;
        }
      }

      switch(FD_ISSET(socket->socket, &select_recv_fd)) {

        case 0:
          break;

        default:

          if(socket->direction == MIDNIGHTCODE_SOCKET_DIR_LISTEN) {
            if(socket->protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {
              midnightcode_socket_accept(sockets, socket);
              continue;
            }
          }

          if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
            receive_data_queue = socket->receive_chunks;
          } else {
            receive_data_queue = socket->receive_data_queue;
          }

          if(midnightcode_list_count(receive_data_queue) >= 
            MIDNIGHTCODE_SOCKET_WIRE_LIST_MAX)
            break;

          receive_buffer_size = MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX;
          receive_buffer = malloc(receive_buffer_size);
          if(!receive_buffer)
            break;
          memset(receive_buffer, 0, receive_buffer_size);

          if(socket->protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {
            receive_octets = recv(socket->socket,
              receive_buffer, receive_buffer_size, 0);
          } else {
            receive_octets = recvfrom(socket->socket,
              receive_buffer, receive_buffer_size, 0,
              (struct sockaddr *)&(socket->ipv4_peer),
              (socklen_t *)&sockaddr_len);
            /* TODO: Consider making this a UDP _accept variant */
            if(receive_octets >= 0) {
              if(socket->direction == MIDNIGHTCODE_SOCKET_DIR_LISTEN) {
                socket->ipv4_remote.s_addr = socket->ipv4_peer.sin_addr.s_addr;
                socket->port_remote = ntohs(socket->ipv4_peer.sin_port);
                snprintf(socket->ipv4_remote_s, 32, "%s",
                  inet_ntoa(socket->ipv4_remote));
                socket->direction = MIDNIGHTCODE_SOCKET_DIR_RECEIVE;
              }
            }
          }
          if(receive_octets == 0) {
            free(receive_buffer);
            break;
          }
          if(receive_octets < 0) {
            free(receive_buffer);
            if(errno == EBADF || errno == ECONNREFUSED ||
              errno == ENOTCONN || errno == ENOTSOCK ||
              errno == EINVAL || errno == ENOMEM) {
              midnightcode_socket_closeshut(sockets, socket,
                MIDNIGHTCODE_SOCKET_STATE_CLOSE);
              break;
            }
          }
          if(receive_octets < receive_buffer_size) {
            receive_buffer = realloc(receive_buffer, receive_octets);
            if(!receive_buffer)
              break;
          }
          receive_data = malloc(sizeof(struct
            midnightcode_socket_queue_element_t));
          memset(receive_data, 0, sizeof(struct
            midnightcode_socket_queue_element_t));
          receive_data->buffer = receive_buffer;
          receive_data->buffer_size = receive_octets;

          midnightcode_list_append(receive_data_queue, receive_data);
          break;
      }

      if(socket->direction == MIDNIGHTCODE_SOCKET_DIR_LISTEN)
        continue;

      if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {
        if(socket->receive_chunks != NULL) {
          if(midnightcode_list_count(socket->receive_chunks) > 0) {
            /* TODO: Consider the value of error handling this call */
            midnightcode_socket_receive_processor(sockets, socket);
          }
        }
        if(socket->receive_control_queue != NULL) {
          if(midnightcode_list_count(socket->receive_control_queue) > 0) {
            /* TODO: Consider the value of error handling this call */
            midnightcode_socket_control_processor(sockets, socket);
          }
        }
      }

      /* TODO: consider impact of waiting for empty kernel send queue */
      switch(FD_ISSET(socket->socket, &select_send_fd)) {

        case 0:
          break;

        default:

          if(socket->control == MIDNIGHTCODE_SOCKET_CONTROL_MIDNIGHTCODE) {

            send_control_queue = socket->send_control_queue;
            if(midnightcode_list_count(send_control_queue) > 0) {
              midnightcode_list_for_each(send_control, send_control_queue) {
                if(send_control->buffer_size <=
                  MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX) {
                  if(socket->protocol == MIDNIGHTCODE_SOCKET_PROTOCOL_TCP) {
                    send_octets = send(socket->socket, send_control->buffer,
                      send_control->buffer_size, 0);
                  }
                  if(send_octets < 0) {
                    if(errno == EBADF || errno == ECONNRESET ||
                      errno == EDESTADDRREQ || errno == EINVAL ||
                      errno == EISCONN || errno == EMSGSIZE ||
                      errno == ENOMEM || errno == ENOTCONN ||
                      errno == ENOTSOCK || errno == EOPNOTSUPP ||
                      errno == EPIPE) {
                      midnightcode_socket_closeshut(sockets, socket,
                        MIDNIGHTCODE_SOCKET_STATE_CLOSE);
                    }
                    break;
                  } else {
                    old_send_control = send_control;
                    midnightcode_list_remove(send_control_queue, send_control);
                    if(old_send_control) {
                      if(old_send_control->buffer)
                        free(old_send_control->buffer);
                      free(old_send_control);
                    }
                    break;
                  }
                }
              }
            }
          }

          /* TODO: consider a generic send function (for control and data) */
          send_data_queue = socket->send_data_queue;
          if(midnightcode_list_count(send_data_queue) > 0) {
            midnightcode_list_for_each(send_data, send_data_queue) {
              if(send_data->buffer_size <=
                MIDNIGHTCODE_SOCKET_WIRE_BLOCK_MAX) {
                send_octets = send(socket->socket, send_data->buffer,
                  send_data->buffer_size, 0);
                if(send_octets < 0) {
                  if(errno == EBADF || errno == ECONNRESET ||
                    errno == EDESTADDRREQ || errno == EINVAL ||
                    errno == EISCONN || errno == EMSGSIZE ||
                    errno == ENOMEM || errno == ENOTCONN ||
                    errno == ENOTSOCK || errno == EOPNOTSUPP ||
                    errno == EPIPE) {
                    midnightcode_socket_closeshut(sockets, socket,
                      MIDNIGHTCODE_SOCKET_STATE_CLOSE);
                  }
                  break;
                } else {
                  old_send_data = send_data;
                  midnightcode_list_remove(send_data_queue, send_data);
                  if(old_send_data) {
                    if(old_send_data->buffer)
                      free(old_send_data->buffer);
                    free(old_send_data);
                  }
                  break;
                }
              }
            }
          }
          break;
      }
    }

    pthread_mutex_unlock(&midnightcode_socket_mutex_t);
    pthread_testcancel();
    usleep(MIDNIGHTCODE_SOCKET_POLL_TIME_HANDLER);

  }

}



int
midnightcode_socket_initialise(struct midnightcode_socket_head_t * sockets) {
  int ret_i;


  if(!sockets)
    return -1;
  if(midnightcode_socket_sockets != NULL)
    return -1;

  midnightcode_socket_sockets = sockets;
  signal(SIGPIPE, SIG_IGN);

  ret_i = 0;
  if(pthread_create(&midnightcode_socket_pthread_t, NULL,
    midnightcode_socket_handler, NULL)) {
    midnightcode_socket_sockets = sockets;
    ret_i = -1;
  }


  return ret_i;
}



int
midnightcode_socket_shutdown(struct midnightcode_socket_head_t * sockets) {
  struct midnightcode_socket_element_t * socket;
  int ret_i;


  if(!sockets)
    return -1;
  if(sockets != midnightcode_socket_sockets)
    return -1;
  if(midnightcode_socket_sockets == NULL)
    return -1;

  ret_i = 0;

  if(pthread_cancel(midnightcode_socket_pthread_t)) {
    ret_i = -1;

  } else {

    midnightcode_list_for_each(socket, sockets) {
      midnightcode_socket_closeshut(sockets, socket,
        MIDNIGHTCODE_SOCKET_STATE_CLOSE);
    }
    sleep(1);
    midnightcode_list_for_each(socket, sockets) {
      midnightcode_socket_closeshut(sockets, socket,
        MIDNIGHTCODE_SOCKET_STATE_USER_SHUT);
    }
    sleep(1);
    midnightcode_list_for_each(socket, sockets) {
      midnightcode_socket_closeshut(sockets, socket,
        MIDNIGHTCODE_SOCKET_STATE_DESTROY);
    }
  }

  midnightcode_socket_sockets = NULL;


  return ret_i;
}


