/* * channel.c * * Channel object creation and deletion code * * Copyright (c) 2004 Todd MacDermid * */ #include #include #include /* * wipe_cut_channels deletes all channel objects from the connection's * channels array, preventing memory leaks. No return value. */ void wipe_cut_channels(cutlass_t *cut_handle, conn_t *conn) { int i; for(i = 0; i < CUT_NUM_CHANNELS; i++) { switch(conn->in_channels[i].channel_type) { case CHANNEL_MSG: msg_info_destroy(cut_handle, conn->in_channels[i].channel_info); free(conn->in_channels[i].buffer); break; case CHANNEL_FILE: file_info_destroy(cut_handle, conn->in_channels[i].channel_info); break; case CHANNEL_NONE: /* Do nothing */ break; default: cutlass_sysmsg(cut_handle, CUT_DEBUG, "wipe_cut_channels: Unknown channel_type\n"); break; } switch(conn->out_channels[i].channel_type) { case CHANNEL_MSG: msg_info_destroy(cut_handle, conn->in_channels[i].channel_info); free(conn->in_channels[i].buffer); break; case CHANNEL_FILE: file_info_destroy(cut_handle, conn->in_channels[i].channel_info); break; case CHANNEL_NONE: /* Do nothing */ break; default: cutlass_sysmsg(cut_handle, CUT_DEBUG, "wipe_cut_channels: Unknown channel_type\n"); break; } conn->in_channels[i].channel_type = CHANNEL_NONE; conn->out_channels[i].channel_type = CHANNEL_NONE; } } /* * new_cut_channel_out finds an empty channel number, and allocates a new * channel object, inserting the new object into the connection's channels * array, with the new channel number as the index. * * Returns the channel number on success, or 0 on failure. (0 is not * a valid channel ID). */ uint8_t new_cut_channel_out(cutlass_t *cut_handle, conn_t *conn, uint8_t channel_type, uint32_t channel_size, void *channel_object, uint8_t *contents, uint32_t request_id) { int i; int j; struct channel *channels; channels = conn->out_channels; for(j = 1; j < CUT_NUM_CHANNELS; j++) { i = conn->last_out_channel + j; i = i % CUT_NUM_CHANNELS; if(0 == i) { i++; } if(channels[i].channel_type == CHANNEL_NONE) { memset(&(channels[i]), 0, sizeof(struct channel)); if(channel_type != CHANNEL_FILE) { channels[i].buffer = calloc(1, channel_size); if(NULL == channels[i].buffer) { cutlass_sysmsg(cut_handle, CUT_ERROR, "new_cut_channel_out: error allocating memory\n"); return(0); } memcpy(channels[i].buffer, contents, channel_size); } channels[i].gap_list = gaplist_init(cut_handle, channel_size); if(channels[i].gap_list == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "new_cut_channel_out: gaplist_init failed\n"); return(0); free(channels[i].buffer); } conn->last_out_channel = i; channels[i].channel_type = channel_type; channels[i].channel_info = channel_object; channels[i].channel_id = i; channels[i].data_size = channel_size; channels[i].last_received.tv_sec = 0; channels[i].last_received.tv_nsec = 0; channels[i].last_sent.tv_sec = 0; channels[i].last_sent.tv_nsec = 0; channels[i].request_id = request_id; return(i); } } return(0); } /* * new_cut_channel_in determines if the requested channel_id is avilable, * and if it is, allocates a new channel object of the appropriate type * in at that index. * * Returns 0 on success, 1 if the channel is already allocated to a channel * object of that type (and thus, we likely lost the ack of the * initialization in the network, or -1 if it's currently an active * channel of a different type, -2 on some other error. */ int new_cut_channel_in(cutlass_t *cut_handle, struct channel *channels, uint8_t channel_id, uint8_t channel_type, uint32_t channel_size, void *channel_object) { cutlass_sysmsg(cut_handle, CUT_DEBUG, "new_cut_channel_in - called id %d type %d size %d\n", channel_id, channel_type, channel_size); if(channels[channel_id].channel_type == CHANNEL_NONE) { memset(&(channels[channel_id]), 0, sizeof(struct channel)); if(channel_type != CHANNEL_FILE) { channels[channel_id].buffer = calloc(1, channel_size); if(NULL == channels[channel_id].buffer) { cutlass_sysmsg(cut_handle, CUT_ERROR, "new_cut_channel_in: error allocating memory\n"); return(-2); } } channels[channel_id].gap_list = gaplist_init(cut_handle, channel_size); if(channels[channel_id].gap_list == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "new_cut_channel_in: gaplist_init failed\n"); free(channels[channel_id].buffer); return(-1); } channels[channel_id].channel_type = channel_type; channels[channel_id].channel_info = channel_object; channels[channel_id].channel_id = channel_id; channels[channel_id].data_size = channel_size; channels[channel_id].last_received.tv_sec = 0; channels[channel_id].last_received.tv_nsec = 0; channels[channel_id].last_sent.tv_sec = 0; channels[channel_id].last_sent.tv_nsec = 0; return(0); } else if(channels[channel_id].channel_type == channel_type) { cutlass_sysmsg(cut_handle, CUT_DEBUG, "new_cut_channel_in: Channel already exists\n"); return(1); } else { cutlass_sysmsg(cut_handle, CUT_INFO, "new_cut_channel_in: Attempted to reallocate channel\n"); return(1); } } /* * cutlass_channel_close_send sends a channel_closed packet to the remote side, * letting them know that as far as we're concerned, this channel is * dead. This can either be the last packet of a three-way teardown, * or the equivalent of a RST. */ int cutlass_channel_close_send(cutlass_t *cut_handle, conn_t *conn, int direction, uint8_t channel_id) { struct cutlass_packet_hdr header; uint8_t local_direction; header.cut_type = CUT_CHANNEL_CLOSE; header.channel_id = channel_id; header.length = 1; local_direction = direction; if(cutlass_send_process(cut_handle, conn, &header, &local_direction, 1) != 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_channel_close_send: cutlass_send failed\n"); return(1); } return(0); } /* * destory_channel frees memory associated with a channel object, * and resets the channel entry in the array. Note that you probably * want to call this through del_cut_channel() rather than directly, * so that the remote side is informed that the channel has gone * away. * * Returns 0 on success, -1 on failure. */ int destroy_channel(cutlass_t *cut_handle, conn_t *conn, int direction, uint8_t channel_id) { struct channel *channel; if((NULL == cut_handle) || (NULL == conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "destroy_channel: Passed a NULL pointer\n"); return(-1); } if(direction == CUT_IN) { channel = &(conn->in_channels[channel_id]); } else if(direction == CUT_OUT) { channel = &(conn->out_channels[channel_id]); } else { cutlass_sysmsg(cut_handle, CUT_ERROR, "destroy_channel: Unknown direction\n"); return(-1); } if(channel->gap_list != NULL) { destroy_gaplist(channel->gap_list); } switch(channel->channel_type) { case CHANNEL_NONE: cutlass_sysmsg(cut_handle, CUT_INFO, "destroy_channel: " "Attempted to delete unallocated channel: %d\n", channel_id); break; case CHANNEL_MSG: msg_info_destroy(cut_handle, channel->channel_info); free(channel->buffer); break; case CHANNEL_FILE: file_info_destroy(cut_handle, channel->channel_info); break; case CHANNEL_REQUEST: free(channel->buffer); break; } if(direction == CUT_IN) { memset(&(conn->in_channels[channel_id]), 0, sizeof (struct channel)); conn->in_channels[channel_id].channel_info = NULL; conn->in_channels[channel_id].channel_type = CHANNEL_NONE; } else { memset(&(conn->out_channels[channel_id]), 0, sizeof (struct channel)); conn->out_channels[channel_id].channel_info = NULL; conn->out_channels[channel_id].channel_type = CHANNEL_NONE; } return(0); } /* * cutlass_channel_close_parse interprets incoming cutlass_channel_close * packets and deletes channels appropriately. */ int cutlass_channel_close_parse(cutlass_t *cut_handle, conn_t *conn, uint8_t *clear_packet, struct cutlass_packet_hdr *packet_info, struct timespec *now) { if((NULL == cut_handle) || (NULL == conn) || (NULL == packet_info)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_channel_close_parse: " "Passed a NULL pointer\n"); return(1); } if(clear_packet[0] == CUT_IN) { destroy_channel(cut_handle, conn, CUT_OUT, packet_info->channel_id); } else if(clear_packet[0] == CUT_OUT) { destroy_channel(cut_handle, conn, CUT_IN, packet_info->channel_id); } else { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_channel_close_parse: " "unknown direction on channel close packet: %d\n", clear_packet[0]); } return(0); } /* * del_cut_channel removes the channel object from the array. The channel_info * object must already have been freed by an appropriate destructor before * calling, as you're about to reset the pointer. This calls destroy_channel * for the heavy lifting, then sends a channel_close packet. * Returns 0 on success, -1 on failure. */ int del_cut_channel(cutlass_t *cut_handle, conn_t *conn, int direction, uint8_t channel_id) { struct channel *channel; if((NULL == cut_handle) || (NULL == conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "del_cut_channel: Passed a NULL pointer\n"); return(-1); } cutlass_sysmsg(cut_handle, CUT_DEBUG, "del_cut_channel: Called on channel %d direction %d\n", channel_id, direction); if(direction == CUT_IN) { channel = &(conn->in_channels[channel_id]); } else if(direction == CUT_OUT) { channel = &(conn->out_channels[channel_id]); } else { cutlass_sysmsg(cut_handle, CUT_ERROR, "del_cut_channel: Unknown direction\n"); return(-1); } if(destroy_channel(cut_handle, conn, direction, channel_id) < 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "del_cut_channel: destroy_channel failed\n"); return(-1); } cutlass_channel_close_send(cut_handle, conn, direction, channel_id); return(0); }