tcpset.c
Go to the documentation of this file.
/*
* Copyright (c) 2011 NLNet Labs. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "config.h"
#include "wire/tcpset.h"
#include <string.h>
static const char* tcp_str = "tcp";
tcp_conn_type*
tcp_conn_create()
{
tcp_conn_type* tcp_conn = NULL;
CHECKALLOC(tcp_conn = (tcp_conn_type*) malloc(sizeof(tcp_conn_type)));
memset(tcp_conn, 0, sizeof(tcp_conn_type));
tcp_conn->packet = buffer_create(PACKET_BUFFER_SIZE);
if (!tcp_conn->packet) {
free(tcp_conn);
return NULL;
}
tcp_conn->msglen = 0;
tcp_conn->total_bytes = 0;
tcp_conn->fd = -1;
return tcp_conn;
}
tcp_set_type*
tcp_set_create()
{
size_t i = 0;
tcp_set_type* tcp_set = NULL;
CHECKALLOC(tcp_set = (tcp_set_type*) malloc(sizeof(tcp_set_type)));
memset(tcp_set, 0, sizeof(tcp_set_type));
tcp_set->tcp_count = 0;
for (i=0; i < TCPSET_MAX; i++) {
tcp_set->tcp_conn[i] = tcp_conn_create();
}
tcp_set->tcp_waiting_first = NULL;
tcp_set->tcp_waiting_last = NULL;
return tcp_set;
}
void
tcp_conn_ready(tcp_conn_type* tcp)
{
ods_log_assert(tcp);
tcp->total_bytes = 0;
tcp->msglen = 0;
buffer_clear(tcp->packet);
}
/*
* Read from a tcp connection.
*
*/
int
tcp_conn_read(tcp_conn_type* tcp)
{
ssize_t received = 0;
ods_log_assert(tcp);
ods_log_assert(tcp->fd != -1);
/* receive leading packet length bytes */
if (tcp->total_bytes < sizeof(tcp->msglen)) {
received = read(tcp->fd, (char*) &tcp->msglen + tcp->total_bytes,
sizeof(tcp->msglen) - tcp->total_bytes);
if (received == -1) {
if (errno == EAGAIN || errno == EINTR) {
/* read would block, try later */
return 0;
} else {
if (errno != ECONNRESET) {
ods_log_error("[%s] error read() sz: %s", tcp_str,
strerror(errno));
}
return -1;
}
} else if (received == 0) {
/* EOF */
return -1;
}
tcp->total_bytes += received;
if (tcp->total_bytes < sizeof(tcp->msglen)) {
/* not complete yet, try later */
return 0;
}
ods_log_assert(tcp->total_bytes == sizeof(tcp->msglen));
tcp->msglen = ntohs(tcp->msglen);
if (tcp->msglen > buffer_capacity(tcp->packet)) {
/* packet to big, drop connection */
ods_log_error("[%s] packet too big, dropping connection", tcp_str);
return 0;
}
buffer_set_limit(tcp->packet, tcp->msglen);
}
ods_log_assert(buffer_remaining(tcp->packet) > 0);
received = read(tcp->fd, buffer_current(tcp->packet),
buffer_remaining(tcp->packet));
if (received == -1) {
if (errno == EAGAIN || errno == EINTR) {
/* read would block, try later */
return 0;
} else {
if (errno != ECONNRESET) {
ods_log_error("[%s] error read(): %s", tcp_str,
strerror(errno));
}
return -1;
}
} else if (received == 0) {
/* EOF */
return -1;
}
tcp->total_bytes += received;
buffer_skip(tcp->packet, received);
if (buffer_remaining(tcp->packet) > 0) {
/* not complete yet, wait for more */
return 0;
}
/* completed */
ods_log_assert(buffer_position(tcp->packet) == tcp->msglen);
return 1;
}
/*
* Write to a tcp connection.
*
*/
int
tcp_conn_write(tcp_conn_type* tcp)
{
ssize_t sent = 0;
ods_log_assert(tcp);
ods_log_assert(tcp->fd != -1);
if (tcp->total_bytes < sizeof(tcp->msglen)) {
uint16_t sendlen = htons(tcp->msglen);
sent = write(tcp->fd, (const char*)&sendlen + tcp->total_bytes,
sizeof(tcp->msglen) - tcp->total_bytes);
if (sent == -1) {
if (errno == EAGAIN || errno == EINTR) {
/* write would block, try later */
return 0;
} else {
return -1;
}
}
tcp->total_bytes += sent;
if (tcp->total_bytes < sizeof(tcp->msglen)) {
/* incomplete write, resume later */
return 0;
}
ods_log_assert(tcp->total_bytes == sizeof(tcp->msglen));
}
ods_log_assert(tcp->total_bytes < tcp->msglen + sizeof(tcp->msglen));
sent = write(tcp->fd, buffer_current(tcp->packet),
buffer_remaining(tcp->packet));
if (sent == -1) {
if (errno == EAGAIN || errno == EINTR) {
/* write would block, try later */
return 0;
} else {
return -1;
}
}
buffer_skip(tcp->packet, sent);
tcp->total_bytes += sent;
if (tcp->total_bytes < tcp->msglen + sizeof(tcp->msglen)) {
/* more to write when socket becomes writable again */
return 0;
}
ods_log_assert(tcp->total_bytes == tcp->msglen + sizeof(tcp->msglen));
return 1;
}
static void
tcp_conn_cleanup(tcp_conn_type* conn)
{
if (!conn) {
return;
}
buffer_cleanup(conn->packet);
free(conn);
}
void
tcp_set_cleanup(tcp_set_type* set)
{
size_t i = 0;
if (!set) {
return;
}
for (i=0; i < TCPSET_MAX; i++) {
tcp_conn_cleanup(set->tcp_conn[i]);
}
free(set);
}
tcp_set_struct::tcp_waiting_last
xfrd_type * tcp_waiting_last
Definition: tcpset.h:70
tcp_set_struct::tcp_count
size_t tcp_count
Definition: tcpset.h:71
buffer_skip
void buffer_skip(buffer_type *buffer, ssize_t count)
Definition: buffer.c:150
buffer_clear
void buffer_clear(buffer_type *buffer)
Definition: buffer.c:99
tcp_set_struct::tcp_conn
tcp_conn_type * tcp_conn[TCPSET_MAX]
Definition: tcpset.h:68
tcp_set_struct::tcp_waiting_first
xfrd_type * tcp_waiting_first
Definition: tcpset.h:69
tcp_conn_write
int tcp_conn_write(tcp_conn_type *tcp)
Definition: tcpset.c:177
tcp_conn_struct::packet
buffer_type * packet
Definition: tcpset.h:58
buffer_current
uint8_t * buffer_current(buffer_type *buffer)
Definition: buffer.c:438
tcp_conn_create
tcp_conn_type * tcp_conn_create()
Definition: tcpset.c:45
buffer_set_limit
void buffer_set_limit(buffer_type *buffer, size_t limit)
Definition: buffer.c:385
buffer_cleanup
void buffer_cleanup(buffer_type *buffer)
Definition: buffer.c:1145
buffer_capacity
size_t buffer_capacity(buffer_type *buffer)
Definition: buffer.c:401
tcp_set_struct
Definition: tcpset.h:67
tcp_conn_struct::msglen
uint16_t msglen
Definition: tcpset.h:56
tcp_set_cleanup
void tcp_set_cleanup(tcp_set_type *set)
Definition: tcpset.c:242
PACKET_BUFFER_SIZE
#define PACKET_BUFFER_SIZE
Definition: buffer.h:50
tcp_conn_struct::total_bytes
uint32_t total_bytes
Definition: tcpset.h:54
tcp_conn_read
int tcp_conn_read(tcp_conn_type *tcp)
Definition: tcpset.c:103
buffer_remaining
size_t buffer_remaining(buffer_type *buffer)
Definition: buffer.c:463
tcp_conn_struct::fd
int fd
Definition: tcpset.h:52
tcpset.h
TCPSET_MAX
#define TCPSET_MAX
Definition: tcpset.h:45
tcp_conn_ready
void tcp_conn_ready(tcp_conn_type *tcp)
Definition: tcpset.c:89
buffer_position
size_t buffer_position(buffer_type *buffer)
Definition: buffer.c:125
tcp_set_create
tcp_set_type * tcp_set_create()
Definition: tcpset.c:67
tcp_conn_struct
Definition: tcpset.h:51
buffer_create
buffer_type * buffer_create(size_t capacity)
Definition: buffer.c:78