/* * connections.c * * Connection creation and deletion code * * Copyright (c) 2004 Todd MacDermid * Kathy Wang * Jack Lloyd * */ #include #include #include #include #include #include #include #include #include /* * cutlass_conn_init() creates a cutlass_connection structure, and initializes * all variables to NULL, zero, or uninitialized state. Allocate any * struct cutlass_connections with this function, rather than malloc() */ conn_t * cutlass_conn_init() { struct cutlass_connection *new_conn; new_conn = (struct cutlass_connection *) calloc(1, sizeof(struct cutlass_connection)); if(new_conn == NULL) return(NULL); new_conn->conn_state = CUTSTATE_UNINITIALZED; new_conn->relay = NULL; cutlass_time(&new_conn->last_packet_received); new_conn->transiting_files_in = NULL; new_conn->transiting_files_out = NULL; new_conn->transiting_msgs_in = NULL; new_conn->transiting_msgs_out = NULL; new_conn->mtu = CUT_DEFAULT_MTU; new_conn->rate = CUT_RATE_INIT; new_conn->requests = hcreate(4); new_conn->keyset = empty_keyset(); new_conn->kex_info = NULL; pthread_mutex_init(&(new_conn->conn_mutex), NULL); return(new_conn); } /* * cutlass_connect is called when we wish to open a new outbound * connection to a remote host. This function is called from the * overlying program. */ int cutlass_connect(cutlass_t *cut_handle, struct sockaddr_in *dest_addr) { char tmp_array[INET_ADDRSTRLEN]; char err_message[CUT_MSG_LEN_MAX]; conn_t *new_conn; int retval; struct timespec now; if((cut_handle == NULL) || (dest_addr == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_add_out: Passed a NULL pointer\n"); return(-1); } new_conn = cutlass_conn_init(); if(new_conn == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_add_out: Could not allocate memory\n"); return(-1); } pthread_mutex_lock(&(new_conn->conn_mutex)); new_conn->socket = socket(AF_INET, SOCK_DGRAM, 0); if(new_conn->socket == -1) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_add_out: Could not create socket\n"); goto fail; } memcpy(&(new_conn->remote_addr), dest_addr, sizeof(struct sockaddr_in)); if((retval = connect(new_conn->socket, (struct sockaddr *)dest_addr, sizeof(struct sockaddr_in))) < 0) { snprintf(err_message, CUT_MSG_LEN_MAX, "cutlass_conn_add_out: Could not connect socket: %d\n", new_conn->socket); cutlass_sysmsg(cut_handle, CUT_ERROR, err_message); close(new_conn->socket); goto fail; } hashtable_fd_add(cut_handle, new_conn); hashtable_addr_add(cut_handle, new_conn); cutlass_fdset_add(cut_handle, new_conn->socket); snprintf( err_message, CUT_MSG_LEN_MAX, "cutlass_connect: " "new connection to %s - port %d on fd %d\n", cutlass_ntop( AF_INET, &(new_conn->remote_addr.sin_addr), tmp_array, INET_ADDRSTRLEN), ntohs(new_conn->remote_addr.sin_port), new_conn->socket); cutlass_sysmsg(cut_handle, CUT_DEBUG, err_message); cutlass_time(&now); cutlass_handshake_init_no_server(cut_handle, new_conn, cut_handle->local_key); pthread_mutex_unlock(&(new_conn->conn_mutex)); return(0); fail: pthread_mutex_unlock(&(new_conn->conn_mutex)); free(new_conn); return(-1); } /* * cutlass_conn_add_in allocates and adds to the requisite data * structures the new connection, created via inbound traffic. This * function is called from the listener. */ conn_t * cutlass_conn_add_in(cutlass_t *cut_handle, struct sockaddr_in *remote_addr) { char err_message[CUT_MSG_LEN_MAX]; char tmp_array[INET_ADDRSTRLEN]; conn_t *new_conn; if((cut_handle == NULL) || (remote_addr == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_add_in: Passed a NULL pointer\n"); return(NULL); } new_conn = cutlass_conn_init(); if(new_conn == NULL) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_add_in: Could not allocate memory\n"); return(NULL); } memcpy(&(new_conn->remote_addr), remote_addr, sizeof(struct sockaddr_in)); new_conn->remote_addr_size = sizeof(struct sockaddr_in); snprintf(err_message, CUT_MSG_LEN_MAX, "cutlass_conn_add_in: new connection from %s on port %d\n", cutlass_ntop(AF_INET, &(new_conn->remote_addr.sin_addr), tmp_array, INET_ADDRSTRLEN), ntohs(new_conn->remote_addr.sin_port)); cutlass_sysmsg(cut_handle, CUT_DEBUG, err_message); hashtable_addr_add(cut_handle, new_conn); cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_add_in locking conn\n"); pthread_mutex_lock(&(new_conn->conn_mutex)); return(new_conn); } /* * cutlass_conn_del removes the connection and frees the memory associated * with the keys. It also sets the appropriate messages to signal the * housekeeping thread to delete the memory of this connection structure */ int cutlass_conn_del(cutlass_t *cut_handle, conn_t *conn) { if((cut_handle == NULL) || (conn == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_del: Passed a NULL handle\n"); return(-1); } conn->conn_state = CUTSTATE_DELETING; if(conn->socket > 0) { shutdown(conn->socket, SHUT_RDWR); cutlass_fdset_del(cut_handle, conn->socket); close(conn->socket); } if(NULL != conn->conn_audio_handle) { cutlass_connection_audio_delete(cut_handle, conn); } pthread_mutex_unlock(&(conn->conn_mutex)); hashtable_conn_del(cut_handle, conn); /* Don't forget to delete all stream keys/objects */ wipe_cut_channels(cut_handle, conn); cutlass_delete_requests(cut_handle, conn); hdestroy(conn->requests); free_keyset(conn->keyset); free_public_key(conn->peer_key); memset(conn, 0, sizeof(conn_t)); housekeeping_conn_delete(cut_handle, conn); return(0); } /* * cutlass_conn_shutdown is an API call used to shutdown an existing * cutlass connection. Returns -1 on failure, 1 on superfluous error * (such as shutting sown a connection that's already shutting down), * or 0 on success. */ int cutlass_conn_shutdown(cutlass_t *cut_handle, uint8_t *fingerprint) { char err_message[CUT_MSG_LEN_MAX]; char tmp_array[INET_ADDRSTRLEN]; conn_t *conn; int retval; struct timespec now; if((cut_handle == NULL) || (fingerprint == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_shutdown: Passed a NULL handle\n"); return(-1); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL == conn) { cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_shutdown: Couldn't find connection\n"); return(-1); } if((conn->conn_state == CUTSTATE_DELETING) || (conn->conn_state == CUTSTATE_CLOSE_SENT) || (conn->conn_state == CUTSTATE_CLOSE_ACKED)) { cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_shutdown unlocking conn in closed state\n"); pthread_mutex_unlock(&(conn->conn_mutex)); return(1); } if(conn->have_info) { snprintf(err_message, CUT_MSG_LEN_MAX, "Closing connection to %s\n", conn->conn_name); cutlass_sysmsg(cut_handle, CUT_INFO, err_message); } else { snprintf(err_message, CUT_MSG_LEN_MAX, "Closing connection to %s\n", cutlass_ntop(AF_INET, &(conn->remote_addr.sin_addr), tmp_array, INET_ADDRSTRLEN)); cutlass_sysmsg(cut_handle, CUT_INFO, err_message); } cutlass_time(&now); conn->conn_state = CUTSTATE_CLOSE_SENT; retval = cutlass_close_send(cut_handle, conn, &now); cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_shutdown unlocking conn normal\n"); pthread_mutex_unlock(&(conn->conn_mutex)); return(retval); } /* * cutlass_close_send is the function responsible for sending the CUT_END * packet signaling the teardown of a connection. Returns -1 on failure, * 0 on success. */ int cutlass_close_send(cutlass_t *cut_handle, conn_t *conn, struct timespec *now) { struct cutlass_packet_hdr header; if((cut_handle == NULL) || (conn == NULL) || (NULL == now)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_close_send: Passed a NULL handle\n"); return(-1); } /* Construct the stuff: connect close packet, no contents */ header.cut_type = CUT_END; header.channel_id = 0; header.length = 0; if(cutlass_send_process(cut_handle, conn, &header, NULL, 0) != 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_close_send: cutlass_send failed\n"); return(-1); } return(0); } /* * cutlass_close_send is the function responsible for sending the CUT_END_ACK * packet acknowledging the teardown of a connection. Returns -1 on failure, * 0 on success. */ int cutlass_close_ack(cutlass_t *cut_handle, conn_t *conn) { struct timespec now; struct cutlass_packet_hdr header; if((cut_handle == NULL) || (conn == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_close_ack: Passed a NULL handle\n"); return(-1); } if(conn->conn_state == CUTSTATE_DELETING) return(1); cutlass_time(&now); /* Construct the stuff: connect close packet, no contents */ header.cut_type = CUT_END_ACK; header.channel_id = 0; header.length = 0; conn->conn_state = CUTSTATE_CLOSE_ACKED; if(cutlass_send_process(cut_handle, conn, &header, NULL, 0) != 0) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_close_send: cutlass_send failed\n"); return(-1); } return(0); } /* * cutlass_conn_fd turns a fingerprint into the file descriptor number * of the socket we're using for this connection. If this is an inbound * only connection, this number will be zero. Returns socket number * on success, -1 on failure. */ int cutlass_conn_fd(cutlass_t *cut_handle, uint8_t *fingerprint) { int fd; conn_t *conn; if((cut_handle == NULL) || (fingerprint == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_fd: Passed a NULL handle\n"); return(-1); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL == conn) return(-1); fd = conn->socket; cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_fd unlocking conn\n"); pthread_mutex_unlock(&conn->conn_mutex); return(fd); } /* * cutlass_conn_addr returns a new address structure for the connection * associated with the fingerprint. This structure must be freed by the * caller. Returrns a pointer to the structure on success, NULL on failure. */ struct sockaddr_in * cutlass_conn_addr(cutlass_t *cut_handle, uint8_t *fingerprint) { struct sockaddr_in *new_addr; conn_t *conn; if((cut_handle == NULL) || (fingerprint == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_addr: Passed a NULL handle\n"); return(NULL); } new_addr = calloc(1, sizeof(struct sockaddr_in)); if(NULL == new_addr) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_addr: Error allocating memory\n"); return(NULL); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL == conn) return(NULL); memcpy(new_addr, &(conn->remote_addr), sizeof(struct sockaddr_in)); cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_addr unlocking conn\n"); pthread_mutex_unlock(&conn->conn_mutex); return(new_addr); } /* * cutlass_conn_name returns a new string containing the nickname for * the connection associated with the fingerprint. This string must be * freed by the caller. Returrns a pointer to the string on success, * NULL on failure. */ char * cutlass_conn_name(cutlass_t *cut_handle, uint8_t *fingerprint) { char *new_string; conn_t *conn; if((cut_handle == NULL) || (fingerprint == NULL)) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_name: Passed a NULL handle\n"); return(NULL); } new_string = calloc(1, CUTLASS_NAME_LEN); if(NULL == new_string) { cutlass_sysmsg(cut_handle, CUT_ERROR, "cutlass_conn_name: Error allocating memory\n"); return(NULL); } conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL == conn) { cutlass_sysmsg(cut_handle, CUT_INFO, "cutlass_conn_name: Fingerprint not found\n"); free(new_string); return(NULL); } strncpy(new_string, conn->conn_name, CUTLASS_NAME_LEN - 1); cutlass_sysmsg(cut_handle, CUT_DEBUG, "cutlass_conn_name unlocking conn\n"); pthread_mutex_unlock(&conn->conn_mutex); return(new_string); } /* * cutlass_conn_debug dumps all the info about the state of the connection * This is for debugging purposes, naturally, so it just goes to stderr. */ /* void cutlass_conn_debug(cutlass_t *cut_handle, uint8_t *fingerprint) { char *tmp_string; conn_t *conn; int i; int fd; struct sockaddr_in *tmp_addr; char tmp_array[INET_ADDRSTRLEN]; conn = hashtable_fingerprint_find(cut_handle, fingerprint); if(NULL == conn) { fprintf(stderr, "No connection found\n"); } else { fprintf(stderr, "Conn ID: "); for(i = 0; i < CUT_ID_LEN/2; i++) { printf("%02X", fingerprint[i]); } tmp_string = cutlass_conn_name(cut_handle, fingerprint); if(NULL == tmp_string) { fprintf(stderr, " - name \"%s\"\n", tmp_string); free(tmp_string); } else { fprintf(stderr, " - No name found\n"); } tmp_addr = cutlass_conn_addr(cut_handle, fingerprint); if(NULL == tmp_addr) { fprintf(stderr, "No related address found\n"); } else { cutlass_ntop(AF_INET, &(tmp_addr->sin_addr), tmp_array, INET_ADDRSTRLEN); if((fd = cutlass_conn_fd(cut_handle, fingerprint)) > 0) { fprintf(stderr, "Connection to %s: ", tmp_array); fprintf(stderr, "to port %d ", ntohs(tmp_addr->sin_port)); fprintf(stderr, "on file descriptor %d\n", fd); } else { fprintf(stderr, "Connection from %s: ", tmp_array); fprintf(stderr, "from port %d\n", ntohs(tmp_addr->sin_port)); } free(tmp_addr); } for (i = 1; i < CUT_NUM_CHANNELS+1; i++) { } } pthread_mutex_unlock(&conn->conn_mutex); } */