diff options
| author | Sam Hartman <hartmans@mit.edu> | 2006-06-22 18:43:20 +0000 |
|---|---|---|
| committer | Sam Hartman <hartmans@mit.edu> | 2006-06-22 18:43:20 +0000 |
| commit | e1c6f641ea03e411ddfa6af9a66e5e3801554a33 (patch) | |
| tree | 1dce165b527fbe4cb89dd551ce2d740f0d7b3ffa /src/lib/ccapi/common/msg.c | |
| parent | c09f8fec46803bc14c6c3b3560459891ff993bb9 (diff) | |
| parent | 90c9fe974de343ffec87efbed89ddcfefd431d9e (diff) | |
| download | krb5-e1c6f641ea03e411ddfa6af9a66e5e3801554a33.tar.gz krb5-e1c6f641ea03e411ddfa6af9a66e5e3801554a33.tar.xz krb5-e1c6f641ea03e411ddfa6af9a66e5e3801554a33.zip | |
r18022@luminous: jaltman | 2006-05-19 05:18:37 -0400
Updates of portable CCAPI sources. All code compiles.
r18025@luminous: jaltman | 2006-05-20 23:48:39 -0400
Construct an outline of a sample platform specific main.c
Add ccs_serv_cleanup() routine. Currently does nothing.
Correct field names used within the authorization check.
r18026@luminous: jaltman | 2006-05-22 02:25:43 -0400
More byte order conversions in the server operations code.
r18027@luminous: jaltman | 2006-05-22 13:12:49 -0400
Source files containing common routines used by both
the client and the server.
r18028@luminous: jaltman | 2006-05-22 13:17:07 -0400
Move msg.c and marshall.c to common library.
Add dllmain.c which contains the Windows DllMain() entry
point. This must be moved later to a Windows specific
directory.
Cleanup type usage and function name references.
All that is missing now from the client DLL is a definition
of cci_perform_rpc() which is the entry point to the IPC
routine.
r18029@luminous: jaltman | 2006-05-22 13:18:27 -0400
Move generic list functions to common/generic_lists.c
so they can be used by both the client dll and the server.
Fix type utilization in serv_ops.c
r18083@luminous: jaltman | 2006-06-05 13:49:34 -0400
improved error handling
r18094@luminous: jaltman | 2006-06-09 10:42:04 -0400
* corrections to windows rpc layer
* corrections to network byte order conversions
r18095@luminous: lxs | 2006-06-09 17:46:24 -0400
cci_msg_retrieve_blob(): changed argument 4 from void** to char**
to suppress the warnings from gcc.
r18096@luminous: lxs | 2006-06-09 17:55:32 -0400
moved enum cc_list_type to generic_lists.h to avoid "incomplete enum"
warnings when compiling generic_lists.c
r18097@luminous: lxs | 2006-06-09 17:56:15 -0400
Removed ancient Metrowerks tests from public headers.
r18098@luminous: lxs | 2006-06-09 17:57:20 -0400
Include string.h to get memcpy, strlen, etc on BSD OSes.
r18099@luminous: lxs | 2006-06-09 17:59:36 -0400
Added Mac OS X project file and Mach-IPC support code
r18106@luminous: jaltman | 2006-06-12 09:56:31 -0400
remove duplicate header file and obsolete source file
r18193@luminous: jaltman | 2006-06-22 12:57:14 -0400
This commit updates lib/krb5/ccache/ccapi to support CCAPI Version 3
and above. Specify -DUSE_CCAPI_V3=1 when compiling to use CCAPI Version 3
r18194@luminous: jaltman | 2006-06-22 12:58:37 -0400
Fix parameter name in cc_credentials_iterator_t next()
r18195@luminous: jaltman | 2006-06-22 12:59:08 -0400
Add missing header
r18196@luminous: jaltman | 2006-06-22 13:11:37 -0400
Use the old exported names for setup and shutdown
r18197@luminous: jaltman | 2006-06-22 13:18:14 -0400
krb5_stdccv3_setup is ok
=== Please enter your commit message above this line ===
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@18200 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/ccapi/common/msg.c')
| -rw-r--r-- | src/lib/ccapi/common/msg.c | 622 |
1 files changed, 622 insertions, 0 deletions
diff --git a/src/lib/ccapi/common/msg.c b/src/lib/ccapi/common/msg.c new file mode 100644 index 000000000..789fc8746 --- /dev/null +++ b/src/lib/ccapi/common/msg.c @@ -0,0 +1,622 @@ +/* $Copyright:
+ *
+ * Copyright 2004-2006 by the Massachusetts Institute of Technology.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require a
+ * specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute
+ * this software and its documentation for any purpose and without fee is
+ * hereby granted, provided that the above copyright notice appear in all
+ * copies and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of M.I.T. not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. Furthermore if you
+ * modify this software you must label your software as modified software
+ * and not distribute it in such a fashion that it might be confused with
+ * the original MIT software. M.I.T. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Individual source code files are copyright MIT, Cygnus Support,
+ * OpenVision, Oracle, Sun Soft, FundsXpress, and others.
+ *
+ * Project Athena, Athena, Athena MUSE, Discuss, Hesiod, Kerberos, Moira,
+ * and Zephyr are trademarks of the Massachusetts Institute of Technology
+ * (MIT). No commercial use of these trademarks may be made without prior
+ * written permission of MIT.
+ *
+ * "Commercial use" means use of a name in a product or other for-profit
+ * manner. It does NOT prevent a commercial firm from referring to the MIT
+ * trademarks in order to convey information (although in doing so,
+ * recognition of their trademark status should be given).
+ * $
+ */
+
+/*
+ * Verifiable, extensible message format.
+ *
+ * Format:
+ * <size of header block (header_len)>
+ * <size of *entire* message, including previous field (flat_len)>
+ * <message type (type)>
+ * <type specific header (header)>
+ * <magic number (magic)>
+ * <data blob 1 length>
+ * <data blob 1>
+ * <data blob 2 length>
+ * <data blob 2>
+ * ...
+ * <magic number (magic)>
+ *
+ * If the header has variable length data it is included in the data blobs.
+ * The header field has the offset from the beginning of the message of the 1st
+ * byte of the data and the length of the data.
+ */
+
+#include "CredentialsCache.h"
+#include "msg.h"
+#include "generic_lists.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * cci_msg_new()
+ *
+ * Purpose: Allocate and initialize a new cc_msg_t structure
+ *
+ * Input parameter (type) in host order
+ *
+ * Return: non-NULL, the msg
+ * NULL, failure
+ *
+ * Errors: ccErrNoMem
+ *
+ */
+cc_int32
+cci_msg_new(cc_uint32 type, cc_msg_t** msgpp)
+{
+ // type should be validated. If invalid set error to ccErrBadParam
+ cc_msg_t* msg;
+
+ if ( type > CC_MSG_MAX_TYPE || msgpp == NULL )
+ return ccErrBadParam;
+
+ msg = (cc_msg_t*)malloc(sizeof(cc_msg_t));
+ if (msg == NULL)
+ return ccErrNoMem;
+
+ msg->type = type;
+ msg->flat = NULL;
+ msg->header = NULL;
+ msg->flat_len = 0;
+ msg->header_len = 0;
+ msg->magic = 0;
+ cci_generic_list_new(&msg->data_blobs);
+ if (msg->data_blobs == NULL) {
+ // pass on error from previous call
+ free(msg);
+ return ccErrNoMem;
+ }
+
+ *msgpp = msg;
+ return ccNoError;
+}
+
+/**
+ * cci_msg_calc_header_size()
+ *
+ * Purpose: Calculates the size of the header
+ *
+ * Return: the size in bytes
+ *
+ * Errors: ccErrBadParam
+ *
+ */
+cc_int32
+cci_msg_calc_header_size(cc_msg_t* msg, cc_uint32 * lenp)
+{
+ int header_len = 12; /* header size, entire size, type */
+
+ if ( msg == NULL || lenp == NULL )
+ return ccErrBadParam;
+
+ header_len += msg->header_len;
+ *lenp = header_len;
+ return ccNoError;
+}
+
+/**
+ * cci_msg_calc_size()
+ *
+ * Purpose: Calculates the size of the message
+ * (does not include the magic bytes)
+ *
+ * Return: the size in bytes
+ *
+ * Errors: ccErrBadParam
+ *
+ */
+cc_int32
+cci_msg_calc_size(cc_msg_t* msg, cc_uint32 * lenp)
+{
+ cc_uint32 flat_len;
+ cc_generic_list_node_t* gen_node;
+ cc_generic_iterate_t* gen_iterator;
+ cc_int32 code;
+
+ if ( msg == NULL || lenp == NULL )
+ return ccErrBadParam;
+
+ code = cci_msg_calc_header_size(msg, &flat_len);
+ if (code != ccNoError)
+ goto bad;
+
+ code = cci_generic_list_iterator(msg->data_blobs, &gen_iterator);
+ if ( code != ccNoError )
+ goto bad;
+
+ while (cci_generic_iterate_has_next(gen_iterator)) {
+ code = cci_generic_iterate_next(gen_iterator, &gen_node);
+ if (code != ccNoError)
+ break;
+ flat_len += gen_node->len + BLOB_LEN;
+ }
+ cci_generic_free_iterator(gen_iterator);
+ if (code != ccNoError)
+ goto bad;
+
+ flat_len += MAGIC_HEAD_LEN + MAGIC_DATA_LEN;
+ *lenp = flat_len;
+
+ bad:
+ return code;
+}
+
+/**
+ * cci_msg_add_data_blob()
+ *
+ * Purpose: Adds 'len' bytes of data to the msg
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_add_data_blob(cc_msg_t* msg, void *data, cc_uint32 len, cc_uint32 *lenp)
+{
+ cc_int32 code;
+
+ if (msg == NULL || data == NULL || len <= 0 || lenp == NULL)
+ return ccErrBadParam;
+
+ code = cci_generic_list_append(msg->data_blobs, data, len, NULL);
+ if ( code != ccNoError )
+ return code;
+ return cci_msg_calc_blob_pos(msg, data, len, lenp);
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_calc_blob_pos(cc_msg_t* msg, void *data, cc_uint32 len, cc_uint32 * posp)
+{
+ cc_uint32 pos;
+ cc_generic_list_node_t* gen_node;
+ cc_generic_iterate_t* gen_iterator;
+ cc_int32 code;
+
+ code = cci_msg_calc_header_size(msg, &pos);
+ pos += sizeof(cc_uint32); /*+ sizeof(cc_uint32) for magic*/
+
+ code = cci_generic_list_iterator(msg->data_blobs, &gen_iterator);
+ while (cci_generic_iterate_has_next(gen_iterator)) {
+ code = cci_generic_iterate_next(gen_iterator, &gen_node);
+ if (gen_node->len != len && gen_node->data != data) {
+ pos += gen_node->len + sizeof(cc_uint32);
+ } else {
+ cci_generic_free_iterator(gen_iterator);
+ *posp = pos + sizeof(cc_uint32);
+ return ccNoError;
+ }
+ }
+
+ cci_generic_free_iterator(gen_iterator);
+ return ccIteratorEnd;
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_add_header(cc_msg_t* msg, void *header, cc_uint32 header_len)
+{
+ if ( msg == NULL || header == NULL )
+ return ccErrBadParam;
+
+ msg->header = header;
+ msg->header_len = header_len;
+ return ccNoError;
+}
+
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_flatten(cc_msg_t* msg, void **flatpp)
+{
+ cc_generic_list_node_t* gen_node;
+ cc_generic_iterate_t* gen_iterator;
+ char *cur_pos;
+ cc_uint32 zero = 0;
+ cc_uint32 magic = 0;
+ cc_uint32 msg_len;
+ cc_uint32 u32;
+ cc_int32 code;
+
+ if (msg == NULL)
+ return ccErrBadParam;
+
+ code = cci_msg_calc_size(msg,&msg->flat_len);
+ if ( code != ccNoError )
+ return code;
+
+ if (msg->flat_len > CC_MSG_MAX_SIZE)
+ return ccErrBadParam;
+
+ msg->flat = (void *)malloc(msg->flat_len);
+ if (msg->flat == NULL)
+ return ccErrNoMem;
+
+ cur_pos = msg->flat;
+
+ u32 = htonl(msg->header_len);
+ memcpy(cur_pos,&u32,sizeof(cc_uint32));
+ cur_pos+=sizeof(cc_uint32);
+
+ u32 = htonl(msg->flat_len);
+ memcpy(cur_pos,&u32,sizeof(cc_uint32));
+ cur_pos+=sizeof(cc_uint32);
+
+ u32 = htonl(msg->type);
+ memcpy(cur_pos,&u32,sizeof(cc_uint32));
+ cur_pos+=sizeof(cc_uint32);
+
+ /* header data is already in network order */
+ memcpy(cur_pos, msg->header, msg->header_len);
+ cur_pos += msg->header_len;
+
+ u32 = htonl(zero);
+ memcpy(cur_pos, &u32, sizeof(cc_uint32)); /*will be magic number later*/
+ cur_pos += sizeof(cc_uint32);
+
+ code = cci_generic_list_iterator(msg->data_blobs, &gen_iterator);
+ if ( code != ccNoError ) {
+ free(msg->flat);
+ return code;
+ }
+
+ while (cci_generic_iterate_has_next(gen_iterator)) {
+ code = cci_generic_iterate_next(gen_iterator, &gen_node);
+ if (code != ccNoError) {
+ free(gen_iterator);
+ free(msg->flat);
+ return code;
+ }
+ u32 = htonl(gen_node->len);
+ memcpy(cur_pos, &u32, sizeof(cc_uint32));
+ cur_pos+=sizeof(cc_uint32);
+
+ /* data already in network order */
+ memcpy(cur_pos, gen_node->data, gen_node->len);
+ cur_pos += gen_node->len;
+ }
+ free(gen_iterator);
+
+ u32 = htonl(zero);
+ memcpy(cur_pos, &u32, sizeof(cc_uint32)); /*magic number will go here later*/
+ cur_pos += sizeof(cc_uint32);
+
+ if (cur_pos - (char *)msg->flat != msg->flat_len) {
+ fprintf(stderr, "ERROR cur_pos - msg->flat = %d\n",msg->flat_len);
+ }
+
+ cci_msg_calc_magic(msg->flat, msg->flat_len, &magic);
+ printf("magic = %d\n",magic);
+
+ cci_msg_calc_header_size(msg, &msg_len);
+ memcpy((char *)msg->flat + msg_len, &magic, sizeof(cc_uint32));
+ memcpy((char *)msg->flat + msg->flat_len - sizeof(cc_uint32), &magic, sizeof(cc_uint32));
+
+ if ( flatpp != NULL )
+ *flatpp = msg->flat;
+
+ return ccNoError;
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_calc_magic(void *flat, int flat_len, cc_uint32 * magicp)
+{
+ cc_uint32 magic = 0;
+ int i;
+
+ for (i = 0; i < flat_len; i += sizeof(cc_uint32)) {
+ magic = magic ^ *(int *)((char *)flat + i);
+ }
+ *magicp = htonl(magic);
+ return ccNoError;
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_verify(void *flat, int flat_len, cc_uint32 * validp)
+{
+ cc_uint32 *magic1, *magic2;
+ cc_uint32 *pheader_len;
+ cc_uint32 header_len;
+ cc_uint32 *ptotal_len;
+ cc_uint32 total_len;
+ cc_uint32 *pblob_len;
+ cc_uint32 blob_len;
+ cc_uint32 *ptype;
+ cc_uint32 type;
+ cc_uint32 num_blobs = 0;
+ cc_uint32 zero = 0;
+ cc_uint32 msg_magic, msg_magic2;
+
+ if (flat == NULL || flat_len <= 0 || validp == NULL)
+ return ccErrBadParam;
+
+ pheader_len = flat;
+ ptotal_len = (cc_uint32 *)((char *)pheader_len + sizeof(cc_uint32));
+ ptype = (cc_uint32 *)((char *)ptotal_len + sizeof(cc_uint32));
+
+ header_len = ntohl(*pheader_len);
+ total_len = ntohl(*ptotal_len);
+ type = ntohl(*ptype);
+
+ if (total_len != flat_len) {
+ *validp = 0;
+ return ccNoError;
+ }
+
+ if (header_len > flat_len) {
+ /*too weak. We could verify header_len against type spec header.*/
+ *validp = 0;
+ return ccNoError;
+ }
+ if (type > CC_MSG_MAX_TYPE) {
+ *validp = 0;
+ return ccNoError;
+ }
+
+ magic1 = (cc_uint32 *)((char *)ptype + sizeof(cc_uint32) + header_len);
+ if ((char *)magic1 - (char *)flat == (flat_len - 8)) {
+ /*There are no data blobs*/
+ magic2 = (cc_uint32 *)((char *)magic1 + sizeof(cc_uint32));
+ num_blobs = 0;
+ } else {
+ pblob_len = (cc_uint32 *)((char *)magic1 + sizeof(cc_uint32));
+ num_blobs = 1;
+ blob_len = ntohl(*pblob_len);
+
+ while (blob_len + sizeof(cc_uint32) + ((char *)pblob_len - (char *)flat) < (flat_len - sizeof(cc_uint32))) {
+ pblob_len = (cc_uint32 *)((char *)pblob_len + blob_len + sizeof(cc_uint32));
+ num_blobs++;
+ blob_len = ntohl(*pblob_len);
+ }
+
+ if (blob_len + sizeof(cc_uint32) + ((char *)pblob_len - (char *)flat) != (flat_len - sizeof(cc_uint32))) {
+ /*blobs didn't line up*/
+ *validp = 0;
+ return ccNoError;
+ }
+ magic2 = (cc_uint32 *)((char *)pblob_len + blob_len + sizeof(cc_uint32)); /*2nd magic should be directly after the last blob*/
+ }
+
+ if (*magic1 != *magic2) {
+ *validp = 0;
+ return ccNoError;
+ }
+ msg_magic = *magic1;
+
+ printf("%d %d\n", (char *)magic1 - (char *)flat, (char *)magic2 - (char *)flat);
+
+ memcpy(magic1, &zero, sizeof(cc_uint32));
+ memcpy(magic2, &zero, sizeof(cc_uint32));
+ cci_msg_calc_magic(flat, flat_len, &msg_magic2);
+ /* both msg_magic and msg_magic2 are in network order */
+ if (msg_magic != msg_magic2) {
+ *validp = 0;
+ return ccNoError;
+ }
+ memcpy(magic1, &msg_magic, sizeof(cc_uint32));
+ memcpy(magic2, &msg_magic, sizeof(cc_uint32));
+
+ *validp = 1;
+ return ccNoError;
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_unflatten(void *flat, int flat_len, cc_msg_t** msgpp)
+{
+ cc_msg_t* msg;
+ char *cur_pos;
+ cc_uint32 blob_len;
+ char *blob;
+ cc_uint32 valid;
+ cc_int32 code;
+
+ if ( flat == NULL || flat_len <= 0 || msgpp == NULL )
+ return ccErrBadParam;
+
+ code = cci_msg_new(0, &msg);
+ if (code)
+ return code;
+
+ cci_msg_verify(flat, flat_len, &valid);
+ if (valid != 1) {
+ cci_msg_destroy(msg);
+ return ccErrBadParam;
+ }
+
+ cur_pos = flat;
+ msg->flat = flat;
+
+ msg->header_len = ntohl(*(cc_uint32 *)cur_pos);
+ cur_pos += sizeof(cc_uint32);
+
+ msg->flat_len = ntohl(*(cc_uint32 *)cur_pos);
+ cur_pos += sizeof(cc_uint32);
+
+ msg->type = ntohl(*(cc_uint32 *)cur_pos);
+ cur_pos += sizeof(cc_uint32);
+
+ msg->header = (void *)malloc(msg->header_len);
+ if (msg->header == NULL) {
+ cci_msg_destroy(msg);
+ return ccErrNoMem;
+ }
+ memcpy(msg->header, cur_pos, msg->header_len);
+ cur_pos += msg->header_len;
+
+ msg->magic = ntohl(*(cc_uint32 *)cur_pos);
+ cur_pos += sizeof(cc_uint32);
+
+ if (cur_pos - (char *)flat != flat_len - 8) { /*at least 1 blob*/
+ blob_len = ntohl(*(cc_uint32 *)cur_pos);
+ while (blob_len + (cur_pos - (char *)flat) + sizeof(cc_uint32) <= flat_len - sizeof(cc_uint32)) {
+ blob = (void *)malloc(blob_len);
+ if (blob == NULL) {
+ cci_msg_destroy(msg);
+ return ccErrNoMem;
+ }
+ memcpy(blob, cur_pos + sizeof(cc_uint32), blob_len);
+ cci_generic_list_append(msg->data_blobs, blob, blob_len, NULL);
+
+ cur_pos += sizeof(cc_uint32) + blob_len;
+ blob_len = ntohl(*(int *)cur_pos);
+ }
+ }
+ *msgpp = msg;
+ return ccNoError;
+}
+
+cc_int32
+cci_msg_retrieve_blob(cc_msg_t* msg, cc_uint32 blob_offset, cc_uint32 blob_len, char **blobp)
+{
+ cc_generic_iterate_t* gen_iterator;
+ cc_generic_list_node_t* gen_node;
+ void *ret;
+ cc_uint32 blob_pos;
+ cc_int32 code;
+
+ /*Ensure that the message has been unflattened*/
+ if ( msg == NULL || msg->flat == NULL || blob_offset > msg->flat_len ||
+ blob_len > msg->flat_len - blob_offset || blobp == NULL)
+ return ccErrBadParam;
+
+ code = cci_generic_list_iterator(msg->data_blobs, &gen_iterator);
+ while (cci_generic_iterate_has_next(gen_iterator)) {
+ code = cci_generic_iterate_next(gen_iterator, &gen_node);
+ code = cci_msg_calc_blob_pos(msg, gen_node->data, gen_node->len, &blob_pos);
+ if (blob_pos == blob_offset && gen_node->len == blob_len) {
+ free(gen_iterator);
+ ret = (void *)malloc(blob_len);
+ if (ret == NULL)
+ return ccErrNoMem;
+ memcpy(ret,(char *)msg->flat + blob_offset, blob_len);
+ *blobp = ret;
+ return ccNoError;
+ }
+ }
+ free(gen_iterator);
+ return ccIteratorEnd;
+}
+
+/**
+ * cc_msg_
+ *
+ * Purpose:
+ *
+ * Return:
+ *
+ * Errors:
+ *
+ */
+cc_int32
+cci_msg_destroy(cc_msg_t* msg)
+{
+ if (msg->flat != NULL)
+ free(msg->flat);
+ if (msg->header != NULL)
+ free(msg->header);
+ cci_generic_list_destroy(msg->data_blobs);
+ free(msg);
+ return ccNoError;
+}
+
|
