693 lines
18 KiB
C
693 lines
18 KiB
C
/* $Id: gdoi_app_client.c,v 1.1.4.3 2011/12/12 20:43:47 bew Exp $ */
|
|
/* $Source: /nfs/cscbz/gdoi/gdoicvs/gdoi/src/Attic/gdoi_app_client.c,v $ */
|
|
|
|
/*
|
|
* The license applies to all software incorporated in the "Cisco GDOI reference
|
|
* implementation" except for those portions incorporating third party software
|
|
* specifically identified as being licensed under separate license.
|
|
*
|
|
*
|
|
* The Cisco Systems Public Software License, Version 1.0
|
|
* Copyright (c) 2001-2007 Cisco Systems, Inc. All rights reserved.
|
|
* Subject to the following terms and conditions, Cisco Systems, Inc.,
|
|
* hereby grants you a worldwide, royalty-free, nonexclusive, license,
|
|
* subject to third party intellectual property claims, to create
|
|
* derivative works of the Licensed Code and to reproduce, display,
|
|
* perform, sublicense, distribute such Licensed Code and derivative works.
|
|
* All rights not expressly granted herein are reserved.
|
|
* 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.
|
|
* 3. The names Cisco and "Cisco GDOI reference implementation" must not
|
|
* be used to endorse or promote products derived from this software without
|
|
* prior written permission. For written permission, please contact
|
|
* opensource@cisco.com.
|
|
* 4. Products derived from this software may not be called
|
|
* "Cisco" or "Cisco GDOI reference implementation", nor may "Cisco" or
|
|
* "Cisco GDOI reference implementation" appear in
|
|
* their name, without prior written permission of Cisco Systems, Inc.
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, TITLE AND NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
|
|
* SHALL CISCO SYSTEMS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
|
* 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. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO
|
|
* LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH
|
|
* PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH
|
|
* LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR
|
|
* LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THAT
|
|
* EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. FURTHER, YOU
|
|
* AGREE THAT IN NO EVENT WILL CISCO'S LIABILITY UNDER OR RELATED TO
|
|
* THIS AGREEMENT EXCEED AMOUNT FIVE THOUSAND DOLLARS (US)
|
|
* (US$5,000).
|
|
*
|
|
* ====================================================================
|
|
* This software consists of voluntary contributions made by Cisco Systems,
|
|
* Inc. and many individuals on behalf of Cisco Systems, Inc. For more
|
|
* information on Cisco Systems, Inc., please see <http://www.cisco.com/>.
|
|
*
|
|
* This product includes software developed by Ericsson Radio Systems.
|
|
*/
|
|
|
|
/*
|
|
* gdoi_app_client.c - Code to send/receive messages from GDOI
|
|
* applications.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include <netinet/in.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/uio.h>
|
|
#ifdef NOT_LINUX
|
|
#include <sys/sockio.h>
|
|
#endif
|
|
#include <arpa/inet.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/un.h>
|
|
|
|
#include "log.h"
|
|
#include "util.h"
|
|
#include "string.h"
|
|
#include "transport.h"
|
|
#include "attribute.h"
|
|
#include "message.h"
|
|
#include "exchange.h"
|
|
#include "sa.h"
|
|
#include "gdoi_num.h"
|
|
#include "gdoi_app_num.h"
|
|
#include "gdoi_app_client.h"
|
|
#ifdef IEC90_5_SUPPORT
|
|
#include "gdoi_phase2.h" /* To get struct gdoi_kd_decode_arg */
|
|
#include "gdoi_iec90_5_protos.h"
|
|
#endif
|
|
#ifdef SRTP_SUPPORT
|
|
#include "gdoi_phase2.h" /* To get struct gdoi_kd_decode_arg */
|
|
#include "gdoi_srtp_protos.h"
|
|
#endif
|
|
|
|
#define FALSE 0
|
|
#define TRUE 1
|
|
|
|
#define APP_CLIENT_PIPE "/tmp/apps_to_gdoi"
|
|
|
|
extern int sigpiped;
|
|
|
|
#define ATTR_SIZE (50 * ISAKMP_ATTR_VALUE_OFF)
|
|
|
|
struct gdoi_app_group_info_type {
|
|
struct cmd_header hdr;
|
|
int group_id;
|
|
char address[7]; /* Possible address for ID type, depends on app type */
|
|
char pipe_name[80];
|
|
};
|
|
|
|
struct gdoi_app_transport {
|
|
struct transport transport;
|
|
struct gdoi_app_group_info_type gdoi_app_group_info;
|
|
int s;
|
|
int return_s;
|
|
int listening_socket_only;
|
|
int master_client_transport; /* One on which to accept connections */
|
|
};
|
|
|
|
void gdoi_app_remove (struct transport *);
|
|
static void gdoi_app_report(struct transport *);
|
|
static int gdoi_app_fd_set(struct transport *, fd_set *, int);
|
|
static int gdoi_app_fd_isset(struct transport *, fd_set *);
|
|
static void gdoi_app_handle_message(struct transport *);
|
|
|
|
static struct transport_vtbl gdoi_app_transport_vtbl = {
|
|
{ 0 }, "app",
|
|
NULL,
|
|
gdoi_app_remove,
|
|
gdoi_app_report,
|
|
gdoi_app_fd_set,
|
|
gdoi_app_fd_isset,
|
|
gdoi_app_handle_message,
|
|
/* gdoi_app_send_message */ NULL,
|
|
/* gdoi_app_get_dst */ NULL,
|
|
/* gdoi_app_get_src */ NULL
|
|
};
|
|
|
|
void
|
|
gdoi_app_client_init (void)
|
|
{
|
|
int s, ret;
|
|
struct gdoi_app_transport *t = 0;
|
|
struct sockaddr_un pipe;
|
|
mode_t old_umask;
|
|
int on = 1;
|
|
|
|
/*
|
|
* Add the GDOI Application method to the transport list
|
|
*/
|
|
transport_method_add (&gdoi_app_transport_vtbl);
|
|
|
|
/*
|
|
* Create the IPC socket, and add it as a transport session.
|
|
*/
|
|
t = malloc (sizeof *t);
|
|
if (!t)
|
|
{
|
|
log_print ("gdoi_app_client_init: malloc (%d) failed", sizeof *t);
|
|
return;
|
|
}
|
|
|
|
t->transport.vtbl = &gdoi_app_transport_vtbl;
|
|
|
|
s = socket (AF_LOCAL, SOCK_STREAM, 0);
|
|
if (s < 0)
|
|
{
|
|
log_error ("gdoi_app_client_init: socket failed");
|
|
return;
|
|
}
|
|
|
|
ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on));
|
|
if (ret < 0)
|
|
{
|
|
log_error ("gdoi_app_client_init: bind failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Make sure it's not left over from another run.
|
|
*/
|
|
unlink(APP_CLIENT_PIPE);
|
|
|
|
/*
|
|
* The mode of the pipe must be readable by all, so we need to adjust
|
|
* our umask accordingly.
|
|
*/
|
|
old_umask = umask(0044);
|
|
|
|
bzero(&pipe, sizeof(struct sockaddr_un));
|
|
pipe.sun_family = AF_LOCAL;
|
|
strncpy(pipe.sun_path, APP_CLIENT_PIPE, sizeof(pipe.sun_path)-1);
|
|
|
|
ret = bind(s, (struct sockaddr *) &pipe, SUN_LEN(&pipe));
|
|
if (ret < 0)
|
|
{
|
|
log_error ("gdoi_app_client_init: bind failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Reset the process umask for security reasons.
|
|
*/
|
|
(void) umask(old_umask);
|
|
|
|
ret = listen(s, 1024);
|
|
if (ret < 0)
|
|
{
|
|
log_error ("listen failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Set the open socket in the transport structure.
|
|
*/
|
|
t->s = s;
|
|
t->return_s = 0;
|
|
t->listening_socket_only = TRUE;
|
|
t->master_client_transport = TRUE;
|
|
|
|
transport_add (&t->transport);
|
|
transport_reference (&t->transport);
|
|
t->transport.flags |= TRANSPORT_LISTEN;
|
|
}
|
|
|
|
void
|
|
gdoi_app_remove (struct transport *t)
|
|
{
|
|
free (t);
|
|
}
|
|
|
|
static void
|
|
gdoi_app_report (struct transport *t)
|
|
{
|
|
log_print ("gdoi_app_report: Got Here!");
|
|
}
|
|
|
|
/*
|
|
* Set transport T's socket in FDS, return a value useable by select(2)
|
|
* as the number of file descriptors to check.
|
|
*/
|
|
static int
|
|
gdoi_app_fd_set (struct transport *t, fd_set *fds, int bit)
|
|
{
|
|
struct gdoi_app_transport *u = (struct gdoi_app_transport *)t;
|
|
|
|
if (bit)
|
|
FD_SET (u->s, fds);
|
|
else {
|
|
/*
|
|
* Hack! Asssume both sockets need to be cleared.
|
|
* BEW: But this code doesn't seem to be getting called when the pipe is
|
|
* closed .... need to diagnose.
|
|
*/
|
|
log_print ("gdoi_app_fd_set: Clearing sockets.");
|
|
FD_CLR (u->s, fds);
|
|
FD_CLR (u->return_s, fds);
|
|
}
|
|
|
|
return u->s + 1;
|
|
}
|
|
|
|
/* Check if transport T's socket is set in FDS. */
|
|
static int
|
|
gdoi_app_fd_isset (struct transport *t, fd_set *fds)
|
|
{
|
|
struct gdoi_app_transport *u = (struct gdoi_app_transport *)t;
|
|
|
|
return FD_ISSET (u->s, fds);
|
|
}
|
|
|
|
int gdoi_app_decode_attribute (u_int16_t type, u_int8_t *value, u_int16_t len,
|
|
void *arg)
|
|
{
|
|
struct gdoi_app_group_info_type *ptr =
|
|
(struct gdoi_app_group_info_type *) arg;
|
|
|
|
switch (type)
|
|
{
|
|
case GDOI_CLIENT_ATTR_GROUP_ID:
|
|
ptr->group_id = htonl(decode_32(value));
|
|
break;
|
|
case GDOI_CLIENT_ATTR_GROUP_ADDRESS:
|
|
if (len < 7) { /* Largest address is MAC address (6 octets) */
|
|
memcpy(ptr->address, value, len);
|
|
ptr->address[len] = 0; /* Terminate the string */
|
|
} else {
|
|
log_print ("gdoi_app_decode_attribute: Bad address length %d\n", len);
|
|
return -1;
|
|
}
|
|
break;
|
|
case GDOI_CLIENT_ATTR_RETURN_PIPE:
|
|
memcpy(ptr->pipe_name, value, len);
|
|
ptr->pipe_name[len] = 0; /* Terminate the string */
|
|
break;
|
|
default:
|
|
log_print ("gdoi_app_decode_attribute: Attribute not valid: %d",
|
|
type);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
extern LIST_HEAD (transport_list, transport) transport_list;
|
|
|
|
struct gdoi_app_transport *
|
|
gdoi_app_transport_search (int gid)
|
|
{
|
|
struct transport *t;
|
|
struct gdoi_app_transport *u;
|
|
|
|
for (t = LIST_FIRST (&transport_list); t; t = LIST_NEXT (t, link)) {
|
|
if (t->flags & TRANSPORT_LISTEN) {
|
|
/*
|
|
* Restrict the search to GDOI application transports.
|
|
* NOTE: This logic only allows on application client per group.
|
|
*/
|
|
if (!strcmp(t->vtbl->name, gdoi_app_transport_vtbl.name)) {
|
|
u = (struct gdoi_app_transport *)t;
|
|
if (gid == u->gdoi_app_group_info.group_id) {
|
|
/*
|
|
* Got it!
|
|
*/
|
|
return u;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* For now, just stuff the info into a global struct. We can't yet
|
|
* correlate an incoming msg with a finished GDOI session anyway, so
|
|
* have to restrict ourselves to one connection at a time.
|
|
*/
|
|
int
|
|
gdoi_app_parse_msg (char *msg, int msg_len, struct gdoi_app_transport *u)
|
|
{
|
|
struct cmd_header *hdr = (struct cmd_header *)msg;
|
|
|
|
/*
|
|
* Sanity check the header
|
|
*/
|
|
if (hdr->version != 1)
|
|
{
|
|
log_error("App header unsupported version: %d\n", hdr->version);
|
|
return -1;
|
|
}
|
|
u->gdoi_app_group_info.hdr.version = hdr->version;
|
|
if (hdr->command != COMMAND_REQUEST)
|
|
{
|
|
log_error("App header unsupported command: %d\n", hdr->command);
|
|
return -1;
|
|
}
|
|
u->gdoi_app_group_info.hdr.command = hdr->command;
|
|
u->gdoi_app_group_info.hdr.app_proto = hdr->app_proto;
|
|
u->gdoi_app_group_info.hdr.sequence = hdr->sequence;
|
|
u->gdoi_app_group_info.hdr.pid = hdr->pid;
|
|
|
|
attribute_map (((u_int8_t *)msg + sizeof(struct cmd_header)),
|
|
(msg_len - sizeof(struct cmd_header)),
|
|
gdoi_app_decode_attribute,
|
|
&u->gdoi_app_group_info);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
connect_to_client (char *out_fn)
|
|
{
|
|
int s, ret;
|
|
struct sockaddr_un pipe;
|
|
|
|
s = socket (AF_LOCAL, SOCK_STREAM, 0);
|
|
if (s < 0)
|
|
{
|
|
log_error("socket open failed");
|
|
return -1;
|
|
}
|
|
|
|
|
|
bzero(&pipe, sizeof(struct sockaddr_un));
|
|
pipe.sun_family = AF_LOCAL;
|
|
strncpy(pipe.sun_path, out_fn, sizeof(pipe.sun_path)-1);
|
|
|
|
ret = connect(s, (struct sockaddr *) &pipe, sizeof(pipe));
|
|
if (ret < 0)
|
|
{
|
|
log_error("connect failed: %s\n", out_fn);
|
|
return -1;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Clone a listen transport U, record a destination RADDR for outbound use.
|
|
*/
|
|
static struct transport *
|
|
group_app_clone (struct gdoi_app_transport *u, int new_socket)
|
|
{
|
|
struct transport *t;
|
|
struct gdoi_app_transport *u2;
|
|
|
|
t = malloc (sizeof *u);
|
|
if (!t)
|
|
{
|
|
log_error ("group_app_clone: malloc (%d) failed", sizeof *u);
|
|
return 0;
|
|
}
|
|
u2 = (struct gdoi_app_transport *)t;
|
|
|
|
memcpy (u2, u, sizeof *u);
|
|
u2->s = new_socket;
|
|
u2->master_client_transport = FALSE;
|
|
|
|
transport_add (t);
|
|
|
|
t->flags |= TRANSPORT_LISTEN;
|
|
|
|
return t;
|
|
}
|
|
|
|
|
|
/*
|
|
* A message has arrived on transport T's socket. If T is single-ended,
|
|
* clone it into a double-ended transport which we will use from now on.
|
|
* Package the message as we want it and continue processing in the message
|
|
* module.
|
|
*/
|
|
static void
|
|
gdoi_app_handle_message (struct transport *t)
|
|
{
|
|
struct gdoi_app_transport *u = (struct gdoi_app_transport *)t;
|
|
struct transport *client_t;
|
|
struct gdoi_app_transport *client_u;
|
|
struct sockaddr_un from;
|
|
int from_len = sizeof(from);
|
|
struct message *msg;
|
|
struct msghdr sock_msg;
|
|
struct iovec iov[1];
|
|
int c;
|
|
char data_in[80];
|
|
char name[80];
|
|
int ret, count;
|
|
struct cmd_header *hdr;
|
|
|
|
if (u->master_client_transport)
|
|
{
|
|
/*
|
|
* Do accepts on this one.
|
|
*
|
|
* Accept happens after the select has woken.
|
|
* Only do this is this is a new connection on the listening socket.
|
|
*/
|
|
c = accept(u->s, (struct sockaddr *) &from, (socklen_t *)&from_len);
|
|
if (c < 0)
|
|
{
|
|
log_error ("gdoi_app_handle_message: accept failed");
|
|
return;
|
|
}
|
|
/*
|
|
* Make a specialized GDOI Application transport structure out of the
|
|
* incoming transport.
|
|
*/
|
|
client_t = group_app_clone (u, c);
|
|
if (!client_t)
|
|
{
|
|
log_error("gdoi_app_handle_message: group_app_clone failed");
|
|
return;
|
|
}
|
|
client_u = (struct gdoi_app_transport *)client_t;
|
|
} else {
|
|
client_t = t;
|
|
client_u = u;
|
|
c = u->s;
|
|
}
|
|
|
|
/*
|
|
* Read and process the message.
|
|
*/
|
|
sock_msg.msg_name = NULL;
|
|
sock_msg.msg_namelen = 0;
|
|
sock_msg.msg_control = 0;
|
|
sock_msg.msg_controllen = 0;
|
|
iov[0].iov_base = data_in;
|
|
iov[0].iov_len = 80;
|
|
sock_msg.msg_iov = iov;
|
|
sock_msg.msg_iovlen = 1;
|
|
|
|
count = recvmsg (c, &sock_msg, 0);
|
|
if (count < 0)
|
|
{
|
|
log_error("gdoi_app_handle_message: recvmsg failed");
|
|
return;
|
|
}
|
|
if (count == 0)
|
|
{
|
|
/*
|
|
* Assume the problem comes from the transmit pipe closing down.
|
|
*/
|
|
log_print("gdoi_app_handle_message: "
|
|
"app pipe assumed closed. Deleting pipes to/from client");
|
|
ret = close(client_u->s);
|
|
if (ret < 0)
|
|
{
|
|
log_error("gdoi_app_handle_message: close of s failed");
|
|
}
|
|
ret = close(client_u->return_s);
|
|
if (ret < 0)
|
|
{
|
|
log_error("gdoi_app_handle_message: close of return_s failed");
|
|
}
|
|
transport_release(client_t);
|
|
return;
|
|
}
|
|
|
|
ret = gdoi_app_parse_msg (data_in, count, client_u);
|
|
if (ret < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (u->master_client_transport)
|
|
{
|
|
/*
|
|
* If we just created this transport, connect back to the client.
|
|
*/
|
|
client_u->return_s =
|
|
connect_to_client(&client_u->gdoi_app_group_info.pipe_name[0]);
|
|
if (client_u->return_s< 0)
|
|
{
|
|
log_error("gdoi_app_handle_message: connect_to_client failed");
|
|
return;
|
|
}
|
|
client_u->listening_socket_only = FALSE;
|
|
}
|
|
|
|
msg = message_alloc (client_t, (u_int8_t *)data_in, count);
|
|
if (!msg)
|
|
{
|
|
log_error("message_alloc failed");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Kick off IKE based on the group-id passed in the message using msg.
|
|
*
|
|
* HACK! Require a policy named "Group-XXXXX" where XXXXX is the number
|
|
* of the group. This makes it easy to find the right phase 1 to kick off.
|
|
* We need to first parse the message to find the group id.
|
|
*
|
|
* BUG: We should handle re-transmissions gracefully. E.g., don't force a
|
|
* re-registration if one is already in progress.
|
|
*/
|
|
sprintf(name, "Group-%d", client_u->gdoi_app_group_info.group_id);
|
|
hdr = malloc(sizeof(struct cmd_header));
|
|
if (!hdr) {
|
|
log_error("gdoi_app_handle_message: failed to allocated hdr bytes");
|
|
return;
|
|
}
|
|
hdr->pid = client_u->gdoi_app_group_info.hdr.pid;
|
|
hdr->sequence = client_u->gdoi_app_group_info.hdr.sequence;
|
|
|
|
log_print ("gdoi_app_handle_message: Starting exchange %s", name);
|
|
exchange_establish(name, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Deliver the application data back to the correct application.
|
|
*/
|
|
int
|
|
gdoi_app_deliver_app_data (u_int32_t type, struct sa *sa)
|
|
{
|
|
u_int8_t *attr_start, *attr;
|
|
char *buf;
|
|
struct cmd_header *hdr;
|
|
struct gdoi_app_transport *client_u;
|
|
struct proto *proto;
|
|
int buf_len;
|
|
int ret;
|
|
int gid;
|
|
|
|
proto = TAILQ_FIRST (&sa->protos);
|
|
if (!proto)
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: Application SA proto data missing");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Find the first transport asking for key info for this group using the
|
|
* special group name semantic. This is to deal with the HACK! in
|
|
* gdoi_app_handle_message().
|
|
*/
|
|
if (strncmp(sa->name, "Group-", 6))
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: Invalid group name: %s\n",
|
|
sa->name);
|
|
return -1;
|
|
}
|
|
sscanf(sa->name, "Group-%d", &gid);
|
|
client_u = gdoi_app_transport_search(gid);
|
|
if (!client_u)
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: No transport found for "
|
|
"group id %d\n", gid);
|
|
return -1;
|
|
}
|
|
|
|
if (type != client_u->gdoi_app_group_info.hdr.app_proto) {
|
|
log_error ("gdoi_app_deliver_app_data: Protocol mismatch! "
|
|
"Expected:%d, Given by upper layer::%d\n",
|
|
client_u->gdoi_app_group_info.hdr.app_proto, type);
|
|
return -1;
|
|
}
|
|
|
|
if (!(void *)proto->data)
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: Application SA TEK data missing");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Allocate a block for building attributes. It's sized large enough
|
|
* so that we think it will avoid buffer overflows....
|
|
*/
|
|
attr_start = attr = calloc(1, ATTR_SIZE);
|
|
if (!attr_start)
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: malloc failed");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Call an Application-specific function to fill in the rest of the
|
|
* attributes.
|
|
*/
|
|
switch (type) {
|
|
#ifdef SRTP_SUPPORT
|
|
case GDOI_PROTO_SRTP:
|
|
attr = gdoi_srtp_add_attributes(attr, sa);
|
|
break;
|
|
#endif
|
|
#ifdef IEC90_5_SUPPORT
|
|
case GDOI_PROTO_IEC90_5:
|
|
attr = gdoi_iec90_5_add_attributes(attr, sa);
|
|
break;
|
|
#endif
|
|
default:
|
|
log_error ("gdoi_app_deliver_app_data: No attribute support for "
|
|
"protocol %d", type);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Format the return message. Copy many of the fields from the originating
|
|
* header to ensure they are the same.
|
|
*/
|
|
buf_len = sizeof(struct cmd_header) + (attr - attr_start);
|
|
buf = malloc(buf_len);
|
|
|
|
hdr = (struct cmd_header *) buf;
|
|
hdr->version = client_u->gdoi_app_group_info.hdr.version;
|
|
hdr->command = COMMAND_REPLY;
|
|
hdr->app_proto = type;
|
|
hdr->sequence = client_u->gdoi_app_group_info.hdr.sequence;
|
|
hdr->pid = client_u->gdoi_app_group_info.hdr.pid;
|
|
hdr->ret_errno = 0;
|
|
|
|
memcpy(buf + sizeof(struct cmd_header), attr_start, (attr - attr_start));
|
|
|
|
free(attr_start);
|
|
/*
|
|
* Send the message.
|
|
*/
|
|
ret = send(client_u->return_s, buf, buf_len, 0);
|
|
if (ret < 0)
|
|
{
|
|
log_error ("gdoi_app_deliver_app_data: send failed");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|