/* * request.c * * Code handling transport layer requests (directory requests, * file pulls, etc). * * Copyright (c) 2005 Todd MacDermid * */ #include #include #include static int cutlass_request_pack(cutlass_t *cut_handle, uint8_t *buffer, request_t *request) { uint8_t *cur_loc; uint16_t tmp_request_len; uint32_t tmp_request_id; uint32_t tmp_max_response; if((NULL == cut_handle) || (NULL == buffer) || (NULL == request)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_pack: Passed a NULL pointer\n"); return(-1); } if((request->request_len > CUTLASS_REQ_LEN_MAX) || (request->request_len < CUTLASS_REQ_LEN_MIN)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_pack: Outsized request len: %d\n", request->request_len); return(-1); } cur_loc = buffer; *cur_loc = request->request_type; cur_loc++; tmp_request_len = htons(request->request_len); memcpy(cur_loc, &tmp_request_len, sizeof(tmp_request_len)); cur_loc += sizeof(tmp_request_len); tmp_request_id = htonl(request->request_id); memcpy(cur_loc, &tmp_request_id, sizeof(tmp_request_id)); cur_loc += sizeof(tmp_request_id); tmp_max_response = htonl(request->max_responses); memcpy(cur_loc, &tmp_max_response, sizeof(tmp_max_response)); cur_loc += sizeof(tmp_max_response); memcpy(cur_loc, request->request_data, request->request_len - CUTLASS_REQ_LEN_MIN); return(0); } static int cutlass_request_unpack(cutlass_t *cut_handle, uint8_t *buffer, uint32_t buffer_size, request_t *request) { uint8_t *cur_loc; uint16_t tmp_request_len; uint32_t tmp_request_id; uint32_t tmp_max_response; if((NULL == cut_handle) || (NULL == buffer) || (NULL == request)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_unpack: Passed a NULL pointer\n"); return(-1); } if(buffer_size < CUTLASS_REQ_LEN_MIN) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_unpack: Passed a NULL pointer\n"); return(-1); } memset(request, 0, sizeof(request_t)); cur_loc = buffer; request->request_type = *cur_loc; cur_loc++; memcpy(&tmp_request_len, cur_loc, sizeof(tmp_request_len)); request->request_len = ntohs(tmp_request_len); cur_loc += sizeof(tmp_request_len); if(request->request_len != buffer_size) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_pack: " "Request len not equal to data received: %d vs %d\n", request->request_len, buffer_size); return(-1); } if((request->request_len > CUTLASS_REQ_LEN_MAX) || (request->request_len < CUTLASS_REQ_LEN_MIN)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_pack: Outsized request len: %d\n", request->request_len); return(-1); } memcpy(&tmp_request_id, cur_loc, sizeof(tmp_request_id)); request->request_id = ntohl(tmp_request_id); cur_loc += sizeof(tmp_request_id); memcpy(&tmp_max_response, cur_loc, sizeof(tmp_max_response)); request->max_responses = ntohl(tmp_max_response); cur_loc += sizeof(tmp_max_response); memcpy(request->request_data, cur_loc, request->request_len - CUTLASS_REQ_LEN_MIN); request->request_data[CUTLASS_REQ_DATA_MAX - 1] = 0; return(0); } /* * cutlass_request_process is the function called to determine how to * handle a request from the remote side. This may involve calling a * subsystem, or it may involve sending a rejection notice. * * Returns 0 on normal exit, -1 on failure. */ int cutlass_request_process(cutlass_t *cut_handle, conn_t *conn, uint8_t *buffer, uint32_t buffer_size) { request_t request; if((NULL == cut_handle) || (NULL == conn) || (NULL == buffer)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_process: Passed a NULL pointer\n"); return(-1); } if(cutlass_request_unpack(cut_handle, buffer, buffer_size, &request) < 0) { if((NULL == cut_handle) || (NULL == conn) || (NULL == buffer)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_process: Failure in parsing request\n"); return(-1); } } switch(request.request_type) { case REQ_DIR_ADVERTISE: case REQ_DIR_UNADVERTISE: case REQ_DIR_LOOKUP_PRINT: case REQ_DIR_LOOKUP_REGEX: case REQ_DIR_SUBSCRIBE: return(cutlass_directory_request_process(cut_handle, conn, &request)); break; case REQ_FILE_NAME: case REQ_FILE_CSUM: return(cutlass_file_request_process(cut_handle, conn, &request)); break; default: cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_request_process: Unknown request_type: %d\n", request.request_type); return(-1); } return(0); } /* * cutlass_request_match attempts to match a received response with * an outstanding request from our side. Returns a pointer to the * request object if one exists, or NULL on error or no match found */ request_t * cutlass_request_match(cutlass_t *cut_handle, conn_t *conn, uint32_t request_id) { request_t *request = NULL; if((NULL == cut_handle) || (NULL == conn) || (NULL == request)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_match: Passed a NULL pointer\n"); return(NULL); } if(hfind(conn->requests, &request_id, sizeof(uint32_t)) == TRUE) { request = hstuff(conn->requests); } return(request); } /* * cutlass_request_new is called to create a new request, insert it into * our local storage, and send it to the remote system. Returns 0 * on success, -1 on failure. */ int cutlass_request_new(cutlass_t *cut_handle, conn_t *conn, request_t *request) { request_t *stored_request; struct channel *transport_info; struct timespec now; uint8_t request_buf[CUTLASS_REQ_LEN_MAX]; uint8_t new_channel; uint32_t *id_index; uint32_t new_id; int has_looped = 0; int continue_insert = 1; int insert_success = 0; if((NULL == cut_handle) || (NULL == conn) || (NULL == request)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_new: Passed a NULL pointer\n"); goto fail; } stored_request = calloc(1, sizeof(request_t)); if(NULL == stored_request) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_new: Error allocating memory\n"); goto fail; } id_index = calloc(1, sizeof(uint32_t)); if(NULL == id_index) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_new: Error allocating memory\n"); goto fail2; } new_id = conn->last_request; while(continue_insert) { new_id++; if(0 == new_id) { new_id++; if(0 == has_looped) { has_looped = 1; } else { continue_insert = 0; } } *id_index = new_id; if(hadd(conn->requests, id_index, sizeof(uint32_t), stored_request) == TRUE) { continue_insert = 0; insert_success = 1; conn->last_request = new_id; } } if(!insert_success) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_new: " "Could not insert request into hashtable\n"); goto fail3; } cutlass_request_pack(cut_handle, request_buf, request); new_channel = new_cut_channel_out(cut_handle, conn, CHANNEL_REQUEST, request->request_len, NULL, request_buf, 0); if(new_channel == 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_new: error allocating channel\n"); cutlass_request_delete(cut_handle, conn, *id_index); return(-1); } transport_info = &(conn->out_channels[new_channel]); cutlass_time(&now); cutlass_transport_init_send(cut_handle, conn, transport_info, &now); return(0); fail3: free(id_index); fail2: free(stored_request); fail: return(-1); } /* * cutlass_request_complete is called when a response is received. If it * is a persistent request type, we may take no action, but if it's a * one-off request (like get this directory entry, or get that file), * we may call cutlass_request_delete to clean up. * * Returns 0 on success, or -1 on failure. */ int cutlass_request_complete(cutlass_t *cut_handle, conn_t *conn, request_t *request) { if((NULL == cut_handle) || (NULL == conn) || (NULL == request)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_complete: Passed a NULL pointer\n"); return(-1); } switch(request->request_type) { case REQ_DIR_ADVERTISE: case REQ_DIR_UNADVERTISE: case REQ_DIR_LOOKUP_PRINT: case REQ_DIR_LOOKUP_REGEX: case REQ_FILE_NAME: case REQ_FILE_CSUM: cutlass_request_delete(cut_handle, conn, request->request_id); break; case REQ_DIR_SUBSCRIBE: /* Do nothing */ break; } return(0); } /* * cutlass_request_delete cleans up the locally allocated memory that * stores the requests. * * Returns 0 on successful deletion, 1 on no such entry, or -1 on failure. */ int cutlass_request_delete(cutlass_t *cut_handle, conn_t *conn, uint32_t request_id) { if((NULL == cut_handle) || (NULL == conn)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_request_delete: Passed a NULL pointer\n"); return(-1); } if(hfind(conn->requests, &request_id, sizeof(uint32_t)) == TRUE) { free(hkey(conn->requests)); free(hstuff(conn->requests)); hdel(conn->requests); return(0); } return(1); } /* * cutlass_delete_requests removes all outstanding requests from * the connection's request hash table. This prevents memory leaks * in the case of connections being deleted */ int cutlass_delete_requests(cutlass_t *cut_handle, conn_t *conn) { int more; if((cut_handle == NULL) || (conn == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_delete_requests: Passed a NULL handle\n"); return(-1); } more = 1; if(hfirst(conn->requests) == TRUE) { while(more == 1) { free(hkey(conn->requests)); free(hstuff(conn->requests)); hdel(conn->requests); if(hnext(cut_handle->connections)== FALSE) { more = 0; } } } return(0); }