Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
11.2.0
11.3.0
1 change: 1 addition & 0 deletions agent/Makefile.frag
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ TEST_BINARIES = \
tests/test_internal_instrument \
tests/test_hash \
tests/test_lib_aws_sdk_php \
tests/test_memcached \
tests/test_mongodb \
tests/test_monolog \
tests/test_mysql \
Expand Down
2 changes: 1 addition & 1 deletion agent/config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ if test "$PHP_NEWRELIC" = "yes"; then
php_error.c php_execute.c php_explain.c php_explain_mysqli.c \
php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c \
php_globals.c php_hash.c php_header.c php_httprequest_send.c \
php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c \
php_internal_instrument.c php_memcached.c php_minit.c php_mshutdown.c php_mysql.c \
php_mysqli.c php_newrelic.c php_nrini.c php_observer.c php_output.c php_pdo.c \
php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c \
php_rinit.c php_rshutdown.c php_samplers.c php_stack.c \
Expand Down
27 changes: 12 additions & 15 deletions agent/php_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,14 +603,17 @@ static size_t num_logging_frameworks
typedef struct _nr_vuln_mgmt_table_t {
const char* package_name;
const char* file_to_check;
size_t file_to_check_len;
nr_vuln_mgmt_enable_fn_t enable;
} nr_vuln_mgmt_table_t;

/* Note that all paths should be in lowercase. */
// clang-format: off
static const nr_vuln_mgmt_table_t vuln_mgmt_packages[] = {
{"Drupal", "drupal/component/dependencyinjection/container.php", nr_drupal_version},
{"Wordpress", "wp-includes/version.php", nr_wordpress_version},
{"Drupal", NR_PSTR("drupal/component/dependencyinjection/container.php"), nr_drupal_version},
{"Wordpress", NR_PSTR("wp-includes/version.php"), nr_wordpress_version},
};
// clang-format: on

static const size_t num_packages
= sizeof(vuln_mgmt_packages) / sizeof(nr_vuln_mgmt_table_t);
Expand Down Expand Up @@ -990,28 +993,22 @@ static void nr_execute_handle_logging_framework(const char* filename,
}
}

#undef STR_AND_LEN

static void nr_execute_handle_package(const char* filename) {
if (NULL == filename || 0 >= nr_strlen(filename)) {
nrl_verbosedebug(NRL_FRAMEWORK, "%s: The file name is NULL",
__func__);
return;
}
char* filename_lower = nr_string_to_lowercase(filename);
static void nr_execute_handle_package(const char* filename,
const size_t filename_len) {
size_t i = 0;

for (i = 0; i < num_packages; i++) {
if (nr_stridx(filename_lower, vuln_mgmt_packages[i].file_to_check) >= 0) {
if (nr_striendswith(STR_AND_LEN(filename),
STR_AND_LEN(vuln_mgmt_packages[i].file_to_check))) {
if (NULL != vuln_mgmt_packages[i].enable) {
vuln_mgmt_packages[i].enable();
}
}
}

nr_free(filename_lower);
}

#undef STR_AND_LEN

/*
* Purpose : Detect library and framework usage from a PHP file.
*
Expand All @@ -1036,7 +1033,7 @@ static void nr_php_user_instrumentation_from_file(const char* filename,
nr_execute_handle_autoload(filename, filename_len);
nr_execute_handle_logging_framework(filename, filename_len TSRMLS_CC);
if (NRINI(vulnerability_management_package_detection_enabled)) {
nr_execute_handle_package(filename);
nr_execute_handle_package(filename, filename_len);
}
}

Expand Down
59 changes: 59 additions & 0 deletions agent/php_internal_instrument.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
#include "php_explain_mysqli.h"
#include "php_file_get_contents.h"
#include "php_globals.h"
#include "php_hash.h"
#include "php_httprequest_send.h"
#include "php_internal_instrument.h"
#include "php_memcached.h"
#include "php_mysql.h"
#include "php_mysqli.h"
#include "php_pdo.h"
Expand Down Expand Up @@ -1531,6 +1533,57 @@ NR_INNER_WRAPPER(memcache_function) {
INTERNAL_FUNCTION_PARAM_PASSTHRU);
}

NR_INNER_WRAPPER(memcached_add_server) {
char* host = NULL;
nr_string_len_t host_len = 0;
zend_long port = 0;
zend_long weight = 0;
int zcaught = 0;

if (SUCCESS
== zend_parse_parameters_ex(
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|ll", &host,
&host_len, &port, &weight) &&
NULL != host) {
nr_php_memcached_create_instance_metric(host, port);
}
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
INTERNAL_FUNCTION_PARAM_PASSTHRU);
if (zcaught) {
zend_bailout();
/* NOTREACHED */
}
}

NR_INNER_WRAPPER(memcached_add_servers) {
zval* servers = NULL;
zval* server = NULL;
int zcaught = 0;

if (SUCCESS
== zend_parse_parameters_ex(
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "a", &servers)) {
if (NULL != servers && Z_TYPE_P(servers) == IS_ARRAY) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(servers), server) {
zval* host = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 0);
zval* port = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 1);
if (nr_php_is_zval_valid_string(host) &&
nr_php_is_zval_valid_integer(port)) {
nr_php_memcached_create_instance_metric(Z_STRVAL_P(host), Z_LVAL_P(port));
}
}
ZEND_HASH_FOREACH_END();
}
}
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
INTERNAL_FUNCTION_PARAM_PASSTHRU);

if (zcaught) {
zend_bailout();
/* NOTREACHED */
}
}

/*
* Handle
* bool redis::connect ( string $host[, int $port = 6379 ... ] )
Expand Down Expand Up @@ -3098,6 +3151,8 @@ NR_OUTER_WRAPPER(memcached_set)
NR_OUTER_WRAPPER(memcached_setbykey)
NR_OUTER_WRAPPER(memcached_setmulti)
NR_OUTER_WRAPPER(memcached_setmultibykey)
NR_OUTER_WRAPPER(memcached_addserver)
NR_OUTER_WRAPPER(memcached_addservers)

NR_OUTER_WRAPPER(redis_append)
NR_OUTER_WRAPPER(redis_bitcount)
Expand Down Expand Up @@ -3511,6 +3566,10 @@ void nr_php_generate_internal_wrap_records(void) {
memcache_function, 0, "set")
NR_INTERNAL_WRAPREC("memcached::setmultibykey", memcached_setmultibykey,
memcache_function, 0, "set")
NR_INTERNAL_WRAPREC("memcached::addserver", memcached_addserver,
memcached_add_server, 0, 0);
NR_INTERNAL_WRAPREC("memcached::addservers", memcached_addservers,
memcached_add_servers, 0, 0);

NR_INTERNAL_WRAPREC("redis::connect", redis_connect, redis_connect, 0,
"connect")
Expand Down
34 changes: 34 additions & 0 deletions agent/php_memcached.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#include "php_memcached.h"
#include "nr_datastore_instance.h"
#include "php_agent.h"

nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
const char* host_or_socket,
zend_long port) {
nr_datastore_instance_t* instance = NULL;
if (port == 0) { // local socket
instance = nr_datastore_instance_create("localhost", host_or_socket, NULL);
} else {
char* port_str = nr_formatf("%ld", (long)port);
instance = nr_datastore_instance_create(host_or_socket, port_str, NULL);
nr_free(port_str);
}
return instance;
}

void nr_php_memcached_create_instance_metric(
const char* host_or_socket,
zend_long port) {
nr_datastore_instance_t* instance
= nr_php_memcached_create_datastore_instance(host_or_socket, port);
char* instance_metric = nr_formatf("Datastore/instance/Memcached/%s/%s",
instance->host, instance->port_path_or_id);
nrm_force_add(NRPRG(txn)->unscoped_metrics, instance_metric, 0);
nr_datastore_instance_destroy(&instance);
nr_free(instance_metric);
}
35 changes: 35 additions & 0 deletions agent/php_memcached.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef PHP_MEMCACHED_HDR
#define PHP_MEMCACHED_HDR

#include "nr_datastore_instance.h"
#include "php_includes.h"

/*
* Purpose : Create a datastore instance metadata for a Memcached server.
*
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
* 2. The memcached port as given as given to Memcached::addServer().
*
* Returns: nr_datastore_instance_t* that the caller is responsible for freeing
*/
nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
const char* host_or_socket,
zend_long port);

/*
* Purpose : Create a memcached instance metric
*
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
* 2. The memcached port as given as given to Memcached::addServer().
*/
extern void nr_php_memcached_create_instance_metric(
const char* host_or_socket,
zend_long port);


#endif
127 changes: 127 additions & 0 deletions agent/tests/test_memcached.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2020 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0
*/
#include "tlib_php.h"
#include "tlib_datastore.h"

#include "php_agent.h"
#include "php_memcached.h"
#include "util_system.h"

tlib_parallel_info_t parallel_info
= {.suggested_nthreads = -1, .state_size = 0};

static char* system_host_name;

static void test_create_datastore_instance(void) {
assert_datastore_instance_equals_destroy(
"named socket",
&((nr_datastore_instance_t){
.host = system_host_name,
.database_name = "unknown",
.port_path_or_id = "/tmp/memcached.sock",
}),
nr_php_memcached_create_datastore_instance("/tmp/memcached.sock", 0));

assert_datastore_instance_equals_destroy(
"empty socket",
&((nr_datastore_instance_t){
.host = system_host_name,
.database_name = "unknown",
.port_path_or_id = "unknown",
}),
nr_php_memcached_create_datastore_instance("", 0));

assert_datastore_instance_equals_destroy(
"empty host",
&((nr_datastore_instance_t){
.host = system_host_name,
.database_name = "unknown",
.port_path_or_id = "unknown",
}),
nr_php_memcached_create_datastore_instance(NULL, 0));

assert_datastore_instance_equals_destroy(
"host.name socket",
&((nr_datastore_instance_t){
.host = "host.name",
.database_name = "unknown",
.port_path_or_id = "11211",
}),
nr_php_memcached_create_datastore_instance("host.name", 11211));

assert_datastore_instance_equals_destroy(
"host and port",
&((nr_datastore_instance_t){
.host = "unknown",
.database_name = "unknown",
.port_path_or_id = "6379",
}),
nr_php_memcached_create_datastore_instance("", 6379));

assert_datastore_instance_equals_destroy(
"NULL socket",
&((nr_datastore_instance_t){
.host = "unknown",
.database_name = "unknown",
.port_path_or_id = "11211",
}),
nr_php_memcached_create_datastore_instance(NULL, 11211));
}

static void test_create_instance_metric(void) {
nrtxn_t* txn;
nrmetric_t* metric;
char* metric_str;
tlib_php_engine_create("");
tlib_php_request_start();
txn = NRPRG(txn);

nr_php_memcached_create_instance_metric("host", 11211);
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/host/11211");
tlib_pass_if_not_null("metric found", metric);

nr_php_memcached_create_instance_metric("", 11211);
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/11211");
tlib_pass_if_not_null("metric found", metric);

nr_php_memcached_create_instance_metric(NULL, 7);
metric = nrm_find(txn->unscoped_metrics, "Datastore/instance/Memcached/unknown/7");
tlib_pass_if_not_null("metric found", metric);

nr_php_memcached_create_instance_metric("path/to/sock", 0);
metric_str = nr_formatf("Datastore/instance/Memcached/%s/path/to/sock", system_host_name);
metric = nrm_find(txn->unscoped_metrics, metric_str);
nr_free(metric_str);
tlib_pass_if_not_null("metric found", metric);

nr_php_memcached_create_instance_metric("", 0);
metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name);
metric = nrm_find(txn->unscoped_metrics, metric_str);
nr_free(metric_str);
tlib_pass_if_not_null("metric found", metric);

// restart the transaction because the next metric is the same as a previous metric
tlib_php_request_end();
tlib_php_request_start();
txn = NRPRG(txn);

nr_php_memcached_create_instance_metric(NULL, 0);
metric_str = nr_formatf("Datastore/instance/Memcached/%s/unknown", system_host_name);
metric = nrm_find(txn->unscoped_metrics, metric_str);
nr_free(metric_str);
tlib_pass_if_not_null("metric found", metric);

tlib_php_request_end();
tlib_php_engine_destroy();
}

void test_main(void* p NRUNUSED) {
system_host_name = nr_system_get_hostname();

test_create_datastore_instance();
test_create_instance_metric();

nr_free(system_host_name);
}
6 changes: 3 additions & 3 deletions axiom/nr_version.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@
#endif

/*
* Current version naming scheme is flowers
* Current version naming scheme is gemstones
*
* dahlia 19Sep2022 (10.1)
* echinacea 03Oct2022 (10.2)
* freesia 03Nov2022 (10.3)
* goldenrod 12Dec2022 (10.4)
Expand All @@ -47,8 +46,9 @@
* yarrow 26Jun2024 (10.22)
* zinnia 30Jul2024 (11.0)
* amethyst 26Aug2024 (11.1)
* bowenite 30Sep2024 (11.2)
*/
#define NR_CODENAME "bowenite"
#define NR_CODENAME "corundum"

const char* nr_version(void) {
return NR_STR2(NR_VERSION);
Expand Down
Loading
Loading