array( $ip_id => $ip ) ) ); if ( ! $lab_data || empty( $lab_data['response']['payload'][ $ip_id ]['reputation'] ) ) { $reputation = LAB_IP_OK; $ip_data = array(); $ip_data['reputation']['value'] = $reputation; $ip_data['reputation']['ttl'] = 600; } else { $reputation = absint( $lab_data['response']['payload'][ $ip_id ]['reputation']['value'] ); $ip_data = $lab_data['response']['payload'][ $ip_id ]; } lab_reputation_update($ip , $ip_data); if ( ! empty( $lab_data['response']['payload'][ $ip_id ]['network']['geo'] ) ) { lab_geo_update($ip, $lab_data['response']['payload'][ $ip_id ]); } return $reputation; } function lab_reputation_update( $ip, $ip_data ) { if ( empty( $ip_data['reputation'] ) ) { return; } if ( ! $ip = filter_var( $ip, FILTER_VALIDATE_IP ) ) { return; } $reputation = absint( $ip_data['reputation']['value'] ); $expires = time() + absint( $ip_data['reputation']['ttl'] ); if ( cerber_db_get_var( 'SELECT COUNT(ip) FROM ' . CERBER_LAB_IP_TABLE . ' WHERE ip = "' . $ip . '"' ) ) { cerber_db_query( 'UPDATE ' . CERBER_LAB_IP_TABLE . ' SET reputation = ' . $reputation . ', expires = ' . $expires . ' WHERE ip = "' . $ip . '"' ); } else { cerber_db_query( 'INSERT INTO ' . CERBER_LAB_IP_TABLE . ' (ip, reputation, expires) VALUES ("' . $ip . '",' . $reputation . ',' . $expires . ')' ); } } /** * Send request to a Cerber Lab node. * * @param array $workload Workload * @param string|int Return this element from the payload if it exists * * @return array|bool */ function lab_api_send_request( $workload = array(), $payload_key = null ) { global $node_delay; $push = lab_get_push(); if ( ! $workload && ! $push ) { return false; } $key = lab_get_key(); if ( $workload && empty( $key[2] ) && ! $push ) { return false; } $request = array( 'key' => $key, 'workload' => $workload, 'push' => $push, 'lang' => crb_get_bloginfo( 'language' ), 'multi' => is_multisite(), 'version' => CERBER_VER, 'PHP' => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, 'sapi' => PHP_SAPI, 'time' => time(), ); $ret = lab_send_request( $request ); // If something goes wrong, take the next closest node if ( ! $ret ) { $ret = lab_send_request( $request ); } elseif ( ( $node_delay * 1000 ) > LAB_DELAY_MAX ) { lab_check_nodes(); // Recheck nodes for further requests } if ( $ret ) { lab_trunc_push(); } if ( $payload_key ) { return crb_array_get( $ret, array( 'response', 'payload', $payload_key ) ); } return $ret; } /** * Send an HTTP request to a node. * * @param $request array * @param null $node_id Node ID if not set, will use the last closest and active node * @param string $scheme http|https * * @return array|bool The response of a node on the success request otherwise false on any error */ function lab_send_request($request, $node_id = null, $scheme = null) { global $node_delay; $node = lab_get_node($node_id); if ( ! $scheme ) { if ( crb_get_settings( 'cerberproto' ) ) { $scheme = 'https'; } else { $scheme = 'http'; } } elseif ($scheme != 'http' || $scheme != 'https') { $scheme = 'https'; } $body = array(); $body['container'] = $request; $body['nodes'] = lab_get_nodes(); $request_body = json_encode($body); if (JSON_ERROR_NONE != json_last_error()) { //'Unable to encode request: '.json_last_error_msg(), array(__FUNCTION__,__LINE__)); return false; } $headers = array( 'Host:'.$node[2], 'Content-Type: application/json', 'Accept: application/json', 'Cerber: '.CERBER_VER, /* 'Authorization: Bearer ' . $fields['key']*/ ); $curl = @curl_init(); // @since 4.32 if ( ! $curl ) { return false; } curl_setopt_array( $curl, array( CURLOPT_URL => $scheme . '://' . $node[2] . '/engine/v1/', CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => $request_body, CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'Cerber Security Plugin ' . CERBER_VER, CURLOPT_CONNECTTIMEOUT => 2, CURLOPT_TIMEOUT => 4, // including CURLOPT_CONNECTTIMEOUT CURLOPT_DNS_CACHE_TIMEOUT => 4 * 3600, CURLOPT_SSL_VERIFYHOST => 2, CURLOPT_SSL_VERIFYPEER => true, CURLOPT_CAINFO => ABSPATH . WPINC . '/certificates/ca-bundle.crt', ) ); $start = microtime( true ); $data = @curl_exec( $curl ); $stop = microtime( true ); $node_delay = $stop - $start; if ( $data ) { $response = lab_parse_response( $data ); } else { $response['status'] = 0; $code = intval( curl_getinfo( $curl, CURLINFO_HTTP_CODE ) ); $response['error'] = 'No connection (' . $code . ')'; //if (!$data) // curl_error($curl) . curl_errno($curl) ); } curl_close( $curl ); //$response = lab_parse_response( $data ); lab_update_node_last( $node[0], array( $node_delay, $response['status'], $response['error'], time(), $scheme, $node[1] ) ); if ( $response['error'] ) { return false; } return $response; } /** * Parse node response and detect possible errors * * @param $response * * @return array|mixed|object */ function lab_parse_response( $response ) { $ret = array( 'status' => 1, 'error' => false ); if ( ! empty( $response ) ) { $ret = json_decode( $response, true ); if ( JSON_ERROR_NONE != json_last_error() ) { $ret['status'] = 0; $ret['error'] = 'JSON ERROR: ' . json_last_error_msg(); } // Is everything is OK? if ( empty( $ret['key'] ) || ! empty( $ret['error'] ) ) { $ret['status'] = 0; // Not OK } } else { $ret['status'] = 0; $ret['error'] = 'No node answer'; } if ( ! isset( $ret['error'] ) ) { $ret['error'] = false; } return $ret; } /** * Return "the best" (closest) node if $node_id is not specified * * @param $node_id integer node ID * @return array first element is ID of closest node, second is an IP address */ function lab_get_node($node_id = null){ $node_id = absint($node_id); if ($node_id) $best_id = $node_id; else $best_id = null; $nodes = lab_get_nodes(); if (!$best_id) { if ( $nodes && ! empty( $nodes['best'] ) ) { $best_id = $nodes['best']; if ( ! $nodes['nodes'][ $best_id ]['last'][1] ) { // this node was not active at the last request unset( $nodes['nodes'][ $best_id ] ); $best_id = lab_best_node( $nodes['nodes'] ); } } } if (!$best_id || $best_id > LAB_NODE_MAX) $best_id = rand(1, LAB_NODE_MAX); $name = 'node' . $best_id . '.cerberlab.net'; $host = null; if ( ! empty( $nodes['nodes'][ $best_id ]['last'] ) ) { $node = $nodes['nodes'][ $best_id ]['last']; if ( $node[5] && ( time() - $node[3] ) < LAB_DNS_TTL ) { $host = $node[5]; } } if ( ! $host ) { $host = @gethostbyname( $name ); } return array($best_id, $host, $name); } /** * Check all nodes and find the closest and active one. * * @param bool $force If true performs checking nodes without checking allowed interval LAB_RECHECK * @param bool $kick_dns If true preload DNS cache to eliminate DNS resolving delay * * @return bool|int */ function lab_check_nodes( $force = false, $kick_dns = false ) { $nodes = lab_get_nodes(); if ( ! $force && isset( $nodes['last_check'] ) && ( time() - $nodes['last_check'] ) < LAB_RECHECK ) { return false; } $nodes['nodes'] = array(); // clean up before testing //update_site_option( '_cerberlab_', $nodes ); cerber_update_set( '_cerberlab_', $nodes ); for ( $i = 1; $i <= LAB_NODE_MAX; $i ++ ) { if ( $kick_dns ) { @gethostbyname( 'node' . $i . '.cerberlab.net' ); } lab_send_request( array( 'test' => 'test', 'key' => 1 ), $i ); } $nodes = lab_get_nodes(); $nodes['best'] = lab_best_node( $nodes['nodes'] ); $nodes['last_check'] = time(); //update_site_option( '_cerberlab_', $nodes ); cerber_update_set( '_cerberlab_', $nodes ); return $nodes['best']; } /** * Find the best (closest) and active node in the list of nodes * * @param array $nodes * * @return int */ function lab_best_node( $nodes = array() ) { $active_nodes = array(); foreach ( $nodes as $id => $data ) { if ( $data['last']['1'] ) { // only active nodes must be in the list $active_nodes[ $id ] = $data['last']['0']; } } if ( $active_nodes ) { asort( $active_nodes ); reset( $active_nodes ); $best_id = key( $active_nodes ); } else { $best_id = 0; // no active nodes found :-( } return $best_id; } /** * Update node status * * @param $node_id * @param array $last * * @return bool */ function lab_update_node_last($node_id, $last = array()) { $nodes = lab_get_nodes(); $nodes['nodes'][$node_id]['last'] = $last; return cerber_update_set('_cerberlab_', $nodes); } function lab_get_nodes() { $nodes = cerber_get_set( '_cerberlab_' ); if ( ! $nodes || ! is_array( $nodes ) ) { $nodes = array(); } return $nodes; } /** * Small diagnostic report about nodes for admin * * @return string Report to show in Dashboard */ function lab_status() { $ret = ''; if ( ! crb_get_settings( 'cerberlab' ) && ! lab_lab() ) { $ret .= '
Cerber Lab connection is disabled
'; } $nodes = lab_get_nodes(); if ( empty( $nodes['nodes'] ) ) { return $ret . 'No information. No request has been made yet.
'; } $tb = array(); foreach ( $nodes['nodes'] as $id => $node ) { $delay = round( 1000 * $node['last'][0] ) . ' ms'; $ago = cerber_ago_time( $node['last'][3] ); $status = $node['last'][1]; if ( $status ) { $status = '' . $status . ''; } else { $status = 'Down'; $delay = 'Unknown'; } if ( $country = lab_get_country( $node['last'][5], false ) ) { $country = cerber_country_name( $country ); } else { $country = ''; } $tb[] = array( $id, $delay, $status, $node['last'][2], $node['last'][5], $country, $ago, $node['last'][4], ); } $ret .= cerber_make_plain_table( $tb, array( 'Node', 'Processing time', 'Operational status', 'Info', 'IP address', 'Location', 'Last request', 'Protocol' ), false, true ); if ( ! empty( $nodes['best'] ) ) { $ret .= 'Closest (fastest) node: ' . $nodes['best'] . '
'; } if ( ! empty( $nodes['last_check'] ) ) { $ret .= 'Last check for all nodes: ' . cerber_ago_time( $nodes['last_check'] ) . '
'; } $key = lab_get_key(); $ret .= 'Site ID: ' . $key[0] . '
'; return $ret; } /** * Check if the Cerber Cloud alive * * @return bool|int The number of active nodes, false otherwise */ function lab_is_cloud_ok(){ $nodes = lab_get_nodes(); if ( ! $nodes ) { return false; } $n = 0; foreach ( $nodes['nodes'] as $id => $node ) { if ($node['last'][1]){ $n++; } } if ($n > 0){ return $n; } return false; } /** * Save data for lab * * @param $ip string IP address * @param $reason_id integer Why IP is malicious * @param $details */ function lab_save_push( $ip, $reason_id, $details = null ) { global $cerber_status; $ip = filter_var( $ip, FILTER_VALIDATE_IP ); if ( ! $ip || is_ip_private( $ip ) || crb_acl_is_white( $ip ) || ! ( crb_get_settings( 'cerberlab' ) || lab_lab() ) ) { return; } $reason_id = absint( $reason_id ); if ( $reason_id == 8 || $reason_id == 9 ) { $details = array( 'uri' => $_SERVER['REQUEST_URI'] ); } elseif ( $reason_id == 100 ) { $details = absint( $cerber_status ); } if ( is_array( $details ) ) { $details = serialize( $details ); } $details = cerber_real_escape( $details ); cerber_db_query( 'INSERT INTO ' . CERBER_LAB_TABLE . ' (ip, reason_id, details, stamp) VALUES ("' . $ip . '",' . $reason_id . ',"' . $details . '",' . time() . ')' ); } /** * Get data for lab * * @return array|bool */ function lab_get_push() { //$result = $wpdb->get_results( 'SELECT * FROM ' . CERBER_LAB_TABLE, ARRAY_A ); $result = cerber_db_get_results( 'SELECT * FROM ' . CERBER_LAB_TABLE, MYSQLI_ASSOC ); if ( $result ) { return array( 'type_1' => $result ); } return false; } function lab_trunc_push(){ cerber_db_query( 'TRUNCATE TABLE ' . CERBER_LAB_TABLE ); cerber_db_query( 'DELETE FROM ' . CERBER_LAB_TABLE ); // TRUNCATE might not work on a weird hosting } function cerber_push_lab() { if ( ! crb_get_settings( 'cerberlab' ) ) { return; } if ( cerber_get_set( '_cerberpush_', null, false ) ) { return; } lab_api_send_request(); cerber_update_set( '_cerberpush_', 1, null, false, time() + LAB_INTERVAL ); } function lab_gen_site_id() { if ( is_multisite() ) { $home = network_home_url(); } else { $home = cerber_get_site_url(); } $home = rtrim( trim( $home ), '/' ); $id = substr( $home, strpos( $home, '//' ) + 2 ); $site_id = md5( $id ); return $site_id; } /** * @since 8.5.6 * @param $site_id string * * @return bool */ function lab_check_site_id( $site_id ) { if ( ! $site_id || $site_id != substr( preg_replace( '/[^A-Z0-9]/i', '', $site_id ), 0, 32 ) ) { return false; } return true; } function lab_get_key( $refresh = false, $nocache = false) { static $key = null; if ( ! isset( $key ) || $nocache ) { $key = cerber_get_set( '_cerberkey_' ); } if ( $refresh || ! $key || ! is_array( $key ) ) { if ( empty( $key ) || ! is_array( $key ) ) { $key = array( '' ); } if ( ! lab_check_site_id( $key[0] ) ) { $key[0] = lab_gen_site_id(); } else { // Fix: WP is installed in a subdirectory, rewrite old, domain-based site ID if ( 2 < substr_count( cerber_get_site_url(), '/' ) ) { $key[0] = lab_gen_site_id(); } } $key[1] = time(); if ( empty( $key[4] ) ) { $key[4] = 'SK//' . str_shuffle( '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' ); } cerber_update_set( '_cerberkey_', $key ); } return $key; } function lab_update_key( $lic, $expires = 0 ) { $key = lab_get_key(); $key[2] = strtoupper( $lic ); $key[3] = absint( $expires ); delete_site_option( '_cerberkey_' ); // old cerber_update_set( '_cerberkey_', $key ); lab_get_key( false, true ); // reload the static cache } function lab_validate_lic( $lic = '', &$msg = '' ) { $msg = ''; $key = lab_get_key(); if ( ! $lic ) { if ( empty( $key[2] ) ) { $msg = '(1)'; return false; } $lic = $key[2]; } $request = array( 'key' => $key, 'validate' => $lic ); $i = LAB_NODE_MAX; while ( ! ( $ret = lab_send_request( $request ) ) && $i > 0 ) { $i --; } if ( ! $ret || ! isset( $ret['response']['expires_gmt'] ) ) { cerber_admin_notice( 'A network error occurred while verifying the license key. Please try again in a couple of minutes.' ); $msg = '(2)'; $expires = 0; } else { $msg = '(3)'; $expires = absint( $ret['response']['expires_gmt'] ); } lab_update_key( $lic, $expires ); if ( ! $expires ) { $msg = '(4)'; return false; } if ( time() > ( $expires + LAB_LICENSE_GRACE ) ) { $msg = '(5)'; return false; } $df = get_option( 'date_format', false ); $gmt_offset = get_option( 'gmt_offset', false ) * 3600; $msg = date_i18n( $df, $gmt_offset + $expires ); return true; } function lab_lab( $with_date = 0 ) { if ( $slave = nexus_get_context() ) { if ( ! $slave->site_key ) { return false; } $exp = $slave->site_key; } else { $key = lab_get_key(); if ( empty( $key[2] ) || empty( $key[3] ) ) { return false; } $exp = $key[3]; } if ( time() > ( $exp + LAB_LICENSE_GRACE ) ) { return false; } if ( ! $with_date ) { return true; } if ( $with_date == 2 ) { return $exp; } $df = get_option( 'date_format', false ); $gmt_offset = get_option( 'gmt_offset', false ) * 3600; return date_i18n( $df, $gmt_offset + $exp ); } function lab_indicator(){ if ( lab_is_cloud_ok() && lab_lab() ) { $key = lab_get_key(); $sid = 'Site ID: '.$key[0]; return '' . $text . '
'; $notice = '' . ' | ' . $msg .
' ' . $more . '
|
' . $notice . '