/*************************************************************************** * Function Name: cfe_web_poll * Description : The entry point function that is called in the background * at polled intervals. It listens for and processes HTTP * requests. * Returns : None. ***************************************************************************/ void cfe_web_poll(void *x) { static int first_time = 1; static int in_cfe_web_poll = 0; PSOCKET_INFO si; int i; if( in_cfe_web_poll == 0 ) { in_cfe_web_poll = 1; /* If this is the first time that this function was called, initialize * the socket info data array. */ if( first_time == 1 ) { first_time = 0; for( i = 0, si = g_socket_info; i < NUM_SOCKETS; i++, si++ ) { si->s = SOCKET_CLOSED; si->state = HTTP_READ_FIRST_HDR; si->web_buf_idx = 0; si->post_content_length = 0; } } /* Check the connection state of each socket. */ for( i = 0, si = g_socket_info; i < NUM_SOCKETS; i++, si++ ) { cfe_web_listen( &g_listen_idx ); if( si->s >= 0 ) { int connflag; tcp_status( si->s, &connflag, NULL, NULL ); if( connflag == TCPSTATUS_CONNECTED ) { cfe_web_bg_process( si ); POLL(); } else if( connflag == TCPSTATUS_NOTCONN ) { console_log("web warning: Unexpected TCP disconnect."); tcp_close(si->s); si->s = SOCKET_CLOSED; si->state = HTTP_READ_FIRST_HDR; si->web_buf_idx = 0; } } } in_cfe_web_poll = 0; } } /* cfe_web_poll */
/*************************************************************************** * Function Name: cfe_web_listen * Description : This function checks to see if TCP listen can be issued * on the HTTP port and issues the listen if it can. * Returns : None. ***************************************************************************/ static void cfe_web_listen( int *listen_idx_ptr ) { static int port = SERVER_PORT; int listen_idx = *listen_idx_ptr; PSOCKET_INFO si = &g_socket_info[listen_idx]; /* If a TCP socket has been opened, check its connection status. */ if( si->s >= 0 ) { int connflag; tcp_status( si->s, &connflag, NULL, NULL ); /* If the socket is connection, set the next socket index to listen for * a TCP connection. */ if( connflag == TCPSTATUS_CONNECTED ) { listen_idx = (listen_idx + 1) % NUM_SOCKETS; si = &g_socket_info[listen_idx]; } } /* If the TCP socket has not been opened, open it and listen for a TCP * connection. */ if( si->s == SOCKET_CLOSED ) { /* Open the socket in non-blocking mode. */ POLL(); if( (si->s = tcp_socket()) >= 0 ) { console_log("web info: Waiting for connection on socket %d.", si->s); if( tcp_listen(si->s, port) != 0 ) console_log("web error: listen error on %d.", si->s); } else { console_log("web error %d: Could not create TCP socket.", si->s); si->s = SOCKET_CLOSED; } } *listen_idx_ptr = listen_idx; } /* cfe_web_listen */
/** * @brief Read data from a network connection, and store the data in the connection context buffer. * @return -1 on general failure, -2 if the connection was reset, or the amount of data that was read. */ int64_t client_read(client_t *client) { int_t counter = 0; ssize_t bytes = 0; bool_t blocking = true; stringer_t *error = NULL; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1 || client_status(client) < 0) { return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Clear the line buffer. client->line = pl_null(); } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until the buffer has data or we get an error. do { blocking = st_length_get(client->buffer) ? false : true; // Read bytes off the network. If data is already in the buffer this should be a non-blocking read operation so we can // return the already buffered data without delay. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } } while (blocking && counter++ < 128 && !st_length_get(client->buffer) && status()); // If there is data in the buffer process it. Otherwise if the buffer is empty and the connection appears to be closed // (as indicated by a return value of 0), then return -1 to let the caller know the connection is dead. if (st_length_get(client->buffer)) { client->status = 1; } else if (!bytes) { client->status = 2; return -2; } return st_length_get(client->buffer); }
/** * @brief Read a line of input from a network client session. * @return -1 on general failure, -2 if the connection was reset, or the length of the current line of input, including the trailing new line character. */ int64_t client_read_line(client_t *client) { ssize_t bytes = 0; int_t counter = 0; stringer_t *error = NULL; bool_t blocking = true, line = false; #ifdef MAGMA_PEDANTIC int_t local = 0; stringer_t *ip = NULL, *cipher = NULL; #endif if (!client || client->sockd == -1) { if (client) client->status = 1; return -1; } // Check for data past the current line buffer. else if (pl_length_get(client->line) && st_length_get(client->buffer) > pl_length_get(client->line)) { // Move the unused data to the front of the buffer. mm_move(st_data_get(client->buffer), st_data_get(client->buffer) + pl_length_get(client->line), st_length_get(client->buffer) - pl_length_get(client->line)); // Update the length. st_length_set(client->buffer, st_length_get(client->buffer) - pl_length_get(client->line)); // Check whether the data we just moved contains a complete line. if (!pl_empty((client->line = line_pl_st(client->buffer, 0)))) { client->status = 1; return pl_length_get(client->line); } } // Otherwise reset the buffer and line lengths to zero. else { st_length_set(client->buffer, 0); client->line = pl_null(); } // Loop until we get a complete line, an error, or the buffer is filled. do { // Read bytes off the network. Skip past any existing data in the buffer. if (client->tls) { // If bytes is zero or below and the library isn't asking for another read, then an error occurred. bytes = tls_read(client->tls, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), blocking); // If zero bytes were read, or a negative value was returned to indicate an error, call tls_erorr(), which will return // NULL if the error can be safely ignored. Otherwise log the output for debug purposes. if (bytes <= 0 && (error = tls_error(client->tls, bytes, MANAGEDBUF(512)))) { #ifdef MAGMA_PEDANTIC cipher = tls_cipher(client->tls, MANAGEDBUF(128)); ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TLS client read operation failed. { ip = %.*s / %.*s / result = %zi%s%.*s }", st_length_int(ip), st_char_get(ip), st_length_int(cipher), st_char_get(cipher), bytes, (error ? " / " : ""), st_length_int(error), st_char_get(error)); #endif client->status = -1; return -1; } // This will occur when the read operation results in a 0, or negative value, but TLS error returns NULL to // indicate it was a transient error. For transient errors we simply set bytes equal to 0 so the read call gets retried. else if (bytes <= 0) { bytes = 0; } } else { errno = 0; bytes = recv(client->sockd, st_char_get(client->buffer) + st_length_get(client->buffer), st_avail_get(client->buffer) - st_length_get(client->buffer), (blocking ? 0 : MSG_DONTWAIT)); // Check for errors on non-SSL reads in the traditional way. if (bytes <= 0 && tcp_status(client->sockd)) { #ifdef MAGMA_PEDANTIC local = errno; ip = ip_presentation(client->ip, MANAGEDBUF(INET6_ADDRSTRLEN)); log_pedantic("TCP client read operation failed. { ip = %.*s / result = %zi / error = %i / message = %s }", st_length_int(ip), st_char_get(ip), bytes, local, strerror_r(local, MEMORYBUF(1024), 1024)); #endif client->status = -1; return -1; } } // We actually read in data, so we need to update the buffer to reflect the amount of data it currently holds. if (bytes > 0) { st_length_set(client->buffer, st_length_get(client->buffer) + bytes); } // Check whether we have a complete line before checking whether the connection was closed. if (!st_empty(client->buffer) && !pl_empty((client->line = line_pl_st(client->buffer, 0)))) { line = true; } } while (!line && counter++ < 128 && st_length_get(client->buffer) != st_avail_get(client->buffer) && status()); if (st_length_get(client->buffer) > 0) { client->status = 1; } return pl_length_get(client->line); }