/*
* Copyright (c) Likewise Software. All rights Reserved.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the license, or (at
* your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy
* of the GNU Lesser General Public License along with this program. If
* not, see .
*
* LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
* TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
* WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
* TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
* LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU
* HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
* TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
* license@likewisesoftware.com
*/
/*
* Module Name:
*
* connection.c
*
* Abstract:
*
* Connection API
* Primary entry points
*
* Authors: Brian Koropoff (bkoropoff@likewisesoftware.com)
*
*/
#include "assoc-private.h"
#include "connection-private.h"
#include "util-private.h"
#include "convert.h"
#include
#include
#include
#include
#include
static LWMsgStatus
lwmsg_connection_construct(
LWMsgAssoc* assoc
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
BAIL_ON_ERROR(status = lwmsg_connection_buffer_construct(&priv->sendbuffer));
BAIL_ON_ERROR(status = lwmsg_connection_buffer_construct(&priv->recvbuffer));
priv->packet_size = 2048;
priv->fd = -1;
priv->mode = LWMSG_CONNECTION_MODE_NONE;
memset(&priv->timeout.establish, 0xFF, sizeof(priv->timeout.establish));
memset(&priv->timeout.message, 0xFF, sizeof(priv->timeout.message));
error:
return status;
}
static void
lwmsg_connection_destruct(
LWMsgAssoc* assoc
)
{
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
LWMsgSessionManager* manager = NULL;
lwmsg_connection_buffer_destruct(&priv->sendbuffer);
lwmsg_connection_buffer_destruct(&priv->recvbuffer);
if (priv->fd != -1)
{
close(priv->fd);
priv->fd = -1;
}
if (priv->endpoint)
{
free(priv->endpoint);
priv->endpoint = NULL;
}
if (priv->sec_token)
{
lwmsg_security_token_delete(priv->sec_token);
priv->sec_token = NULL;
}
if (priv->session)
{
if (lwmsg_assoc_get_session_manager(assoc, &manager))
{
/* This should never happen, really */
abort();
}
if (lwmsg_session_manager_leave_session(manager, priv->session))
{
/* Neither should this */
abort();
}
priv->session = NULL;
}
if (priv->marshal_context)
{
lwmsg_data_context_delete(priv->marshal_context);
}
}
static LWMsgStatus
lwmsg_connection_close(
LWMsgAssoc* assoc
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_CLOSE));
error:
return status;
}
static LWMsgStatus
lwmsg_connection_reset(
LWMsgAssoc* assoc
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_RESET));
error:
return status;
}
static LWMsgStatus
lwmsg_connection_send_msg(
LWMsgAssoc* assoc,
LWMsgMessage* message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
priv->params.message = message;
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_SEND));
error:
return status;
}
static LWMsgStatus
lwmsg_connection_recv_msg(
LWMsgAssoc* assoc,
LWMsgMessage* message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
priv->params.message = message;
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_RECV));
error:
return status;
}
static LWMsgStatus
lwmsg_connection_finish(
LWMsgAssoc* assoc,
LWMsgMessage** message
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (message)
{
priv->params.message = NULL;
}
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_FINISH));
if (message)
{
*message = priv->params.message;
}
error:
return status;
}
static LWMsgStatus
lwmsg_connection_set_nonblock(
LWMsgAssoc* assoc,
LWMsgBool nonblock
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
switch (lwmsg_assoc_get_state(assoc))
{
case LWMSG_ASSOC_STATE_NOT_ESTABLISHED:
case LWMSG_ASSOC_STATE_IDLE:
case LWMSG_ASSOC_STATE_CLOSED:
priv->is_nonblock = nonblock;
break;
default:
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_STATE,
"Cannot set blocking while connection is busy");
}
error:
return status;
}
static LWMsgStatus
lwmsg_connection_get_peer_security_token(
LWMsgAssoc* assoc,
LWMsgSecurityToken** out_token
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (!priv->sec_token)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_INVALID_STATE);
}
*out_token = priv->sec_token;
error:
return status;
}
static LWMsgStatus
lwmsg_connection_get_session(
LWMsgAssoc* assoc,
LWMsgSession** session
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (!priv->session)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_INVALID_STATE);
}
*session = priv->session;
error:
return status;
}
static LWMsgAssocState
lwmsg_connection_get_state(
LWMsgAssoc* assoc
)
{
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
LWMsgBool need_send = LWMSG_FALSE;
LWMsgBool need_recv = LWMSG_FALSE;
switch (priv->state)
{
case CONNECTION_STATE_NONE:
return LWMSG_ASSOC_STATE_NONE;
case CONNECTION_STATE_START:
return LWMSG_ASSOC_STATE_NOT_ESTABLISHED;
case CONNECTION_STATE_ESTABLISHED:
need_send = (priv->sendbuffer.current != NULL || !lwmsg_ring_is_empty(&priv->sendbuffer.fragments));
need_recv = (priv->recvbuffer.current != NULL);
if (need_send && need_recv)
{
return LWMSG_ASSOC_STATE_BLOCKED_SEND_RECV;
}
else if (need_send)
{
return LWMSG_ASSOC_STATE_BLOCKED_SEND;
}
else if (need_recv)
{
return LWMSG_ASSOC_STATE_BLOCKED_RECV;
}
else
{
return LWMSG_ASSOC_STATE_IDLE;
}
case CONNECTION_STATE_FINISH_CONNECT:
return LWMSG_ASSOC_STATE_BLOCKED_SEND;
case CONNECTION_STATE_FINISH_SEND_HANDSHAKE:
return LWMSG_ASSOC_STATE_BLOCKED_SEND_RECV;
case CONNECTION_STATE_FINISH_RECV_HANDSHAKE:
return LWMSG_ASSOC_STATE_BLOCKED_RECV;
case CONNECTION_STATE_CLOSED:
return LWMSG_ASSOC_STATE_CLOSED;
case CONNECTION_STATE_ERROR:
return LWMSG_ASSOC_STATE_ERROR;
default:
return LWMSG_ASSOC_STATE_BUSY;
}
}
static
LWMsgStatus
lwmsg_connection_set_timeout(
LWMsgAssoc* assoc,
LWMsgTimeout type,
LWMsgTime* value
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
LWMsgTime* target = NULL;
if (value != NULL &&
(value->seconds < 0 || value->microseconds < 0))
{
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_PARAMETER,
"Invalid (negative) timeout value");
}
switch (type)
{
case LWMSG_TIMEOUT_MESSAGE:
target = &priv->timeout.message;
break;
case LWMSG_TIMEOUT_ESTABLISH:
target = &priv->timeout.establish;
break;
default:
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_UNSUPPORTED,
"Unsupported timeout type");
}
if (value)
{
*target = *value;
}
else
{
memset(target, 0xFF, sizeof(*target));
}
error:
return status;
}
static
LWMsgStatus
lwmsg_connection_establish(
LWMsgAssoc* assoc,
LWMsgSessionConstructFunction construct,
LWMsgSessionDestructFunction destruct,
void* data
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (!priv->session)
{
priv->params.establish.construct = construct;
priv->params.establish.destruct = destruct;
priv->params.establish.construct_data = data;
BAIL_ON_ERROR(status = lwmsg_connection_run(assoc, CONNECTION_EVENT_ESTABLISH));
}
error:
return status;
}
static LWMsgAssocClass connection_class =
{
.construct = lwmsg_connection_construct,
.destruct = lwmsg_connection_destruct,
.send_msg = lwmsg_connection_send_msg,
.recv_msg = lwmsg_connection_recv_msg,
.close = lwmsg_connection_close,
.reset = lwmsg_connection_reset,
.get_peer_security_token = lwmsg_connection_get_peer_security_token,
.get_session = lwmsg_connection_get_session,
.get_state = lwmsg_connection_get_state,
.set_timeout = lwmsg_connection_set_timeout,
.establish = lwmsg_connection_establish,
.set_nonblock = lwmsg_connection_set_nonblock,
.finish = lwmsg_connection_finish
};
LWMsgStatus
lwmsg_connection_new(
const LWMsgContext* context,
LWMsgProtocol* prot,
LWMsgAssoc** out_assoc
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
LWMsgAssoc* assoc = NULL;
ConnectionPrivate* priv = NULL;
BAIL_ON_ERROR(status = lwmsg_assoc_new(context, prot, &connection_class, sizeof(*priv), &assoc));
priv = CONNECTION_PRIVATE(assoc);
priv->state = CONNECTION_STATE_START;
*out_assoc = assoc;
done:
return status;
error:
goto done;
}
LWMsgStatus
lwmsg_connection_set_packet_size(
LWMsgAssoc* assoc,
size_t size
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (priv->session)
{
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_STATE, "Cannot change packet size of established connection");
}
priv->packet_size = size;
error:
return status;
}
LWMsgStatus
lwmsg_connection_set_fd(
LWMsgAssoc* assoc,
LWMsgConnectionMode mode,
int fd
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (fd < 0)
{
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_PARAMETER, "Invalid file descriptor");
}
if (priv->fd != -1 || priv->endpoint != NULL)
{
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_STATE, "Connection parameters already set");
}
priv->fd = fd;
priv->mode = mode;
error:
return status;
}
LWMsgStatus
lwmsg_connection_set_endpoint(
LWMsgAssoc* assoc,
LWMsgConnectionMode mode,
const char* endpoint
)
{
LWMsgStatus status = LWMSG_STATUS_SUCCESS;
ConnectionPrivate* priv = CONNECTION_PRIVATE(assoc);
if (priv->fd != -1 || priv->endpoint != NULL)
{
ASSOC_RAISE_ERROR(assoc, status = LWMSG_STATUS_INVALID_STATE, "Connection parameters already set");
}
priv->endpoint = strdup(endpoint);
if (!priv->endpoint)
{
BAIL_ON_ERROR(status = LWMSG_STATUS_MEMORY);
}
priv->mode = mode;
error:
return status;
}