Skip to content

Commit

Permalink
Merge pull request #978 from newrelic/dev
Browse files Browse the repository at this point in the history
Release 11.3
  • Loading branch information
ZNeumann authored Oct 18, 2024
2 parents 2321b9e + dc162a4 commit f30642f
Show file tree
Hide file tree
Showing 31 changed files with 781 additions and 162 deletions.
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
19 changes: 19 additions & 0 deletions agent/newrelic-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,25 @@ EOF
if [ -d "${cfg_pfx}/fpm/conf.d" ]; then
pi_inidir_dso="${cfg_pfx}/fpm/conf.d"
fi

#
# Debian can use a mods-available directory to store the ini files.
# It creates a symlink from the ini file in the conf.d directory that
# our installer can fail to find (because the symlink is prefixed with
# "20-" (notably the number can change based on configurations).
# While this install script will not install into the mods-available
# directory, our .deb installer can. Therefore, we want to detect if
# newrelic has previously been installed in the mods-available directory
# so that we do not create an additional ini file -- which would result in
# the conf.d directory having both newrelic.ini and 20-newrelic.ini.
#

if [ -d "${cfg_pfx}/mods-available" -a -f "${cfg_pfx}/mods-available/newrelic.ini" ]; then
pi_inidir_cli="${cfg_pfx}/mods-available"
if [ -n "${pi_inidir_dso}" ]; then
pi_inidir_dso="${cfg_pfx}/mods-available"
fi
fi
fi
done

Expand Down
82 changes: 12 additions & 70 deletions agent/php_execute.c
Original file line number Diff line number Diff line change
Expand Up @@ -523,59 +523,7 @@ static nr_library_table_t libraries[] = {
*/
{"Laminas_Http", NR_PSTR("laminas-http/src/client.php"), nr_laminas_http_enable},

/*
* Other frameworks, detected only, but not specifically
* instrumented. We detect these as libraries so that we don't prevent
* detection of a supported framework or library later (since a transaction
* can only have one framework).
*/
{"Aura1", NR_PSTR("aura/framework/system.php"), NULL},
{"Aura2", NR_PSTR("aura/di/src/containerinterface.php"), NULL},
{"Aura3", NR_PSTR("aura/di/src/containerconfiginterface.php"), NULL},
{"CakePHP3", NR_PSTR("cakephp/src/core/functions.php"), NULL},
{"Fuel", NR_PSTR("fuel/core/classes/fuel.php"), NULL},
{"Lithium", NR_PSTR("lithium/core/libraries.php"), NULL},
{"Phpbb", NR_PSTR("phpbb/request/request.php"), NULL},
{"Phpixie2", NR_PSTR("phpixie/core/classes/phpixie/pixie.php"), NULL},
{"Phpixie3", NR_PSTR("phpixie/framework.php"), NULL},
{"React", NR_PSTR("react/event-loop/src/loopinterface.php"), NULL},
{"SilverStripe", NR_PSTR("injector/silverstripeinjectioncreator.php"), NULL},
{"SilverStripe4", NR_PSTR("silverstripeserviceconfigurationlocator.php"), NULL},
{"Typo3", NR_PSTR("classes/typo3/flow/core/bootstrap.php"), NULL},
{"Typo3", NR_PSTR("typo3/sysext/core/classes/core/bootstrap.php"), NULL},

/*
* Other CMS (content management systems), detected only, but
* not specifically instrumented.
*/
{"Moodle", NR_PSTR("moodlelib.php"), NULL},
/*
* It is likely that this will never be found, since the CodeIgniter.php
* will get loaded first, and as such mark this transaction as belonging to
* CodeIgniter, and not Expession Engine.
*/
{"ExpressionEngine", NR_PSTR("system/expressionengine/config/config.php"), NULL},
/*
* ExpressionEngine 5, however, has a very obvious file we can look for.
*/
{"ExpressionEngine5", NR_PSTR("expressionengine/boot/boot.php"), NULL},
/*
* DokuWiki uses doku.php as an entry point, but has other files that are
* loaded directly that this won't pick up. That's probably OK for
* supportability metrics, but we'll add the most common name for the
* configuration file as well just in case.
*/
{"DokuWiki", NR_PSTR("doku.php"), NULL},
{"DokuWiki", NR_PSTR("conf/dokuwiki.php"), NULL},

/*
* SugarCRM no longer has a community edition, so this likely only works
* with older versions.
*/
{"SugarCRM", NR_PSTR("sugarobjects/sugarconfig.php"), NULL},

{"Xoops", NR_PSTR("class/xoopsload.php"), NULL},
{"E107", NR_PSTR("e107_handlers/e107_class.php"), NULL},
};
// clang-format: on

Expand All @@ -590,9 +538,6 @@ static nr_library_table_t logging_frameworks[] = {
/* laminas-log - Logging for PHP */
{"laminas-log", NR_PSTR("laminas-log/src/logger.php"), NULL},
/* cakephp-log - Logging for PHP */
{"cakephp-log", NR_PSTR("cakephp/log/log.php"), NULL},
/* Analog - Logging for PHP */
{"Analog", NR_PSTR("analog/analog.php"), NULL},
};
// clang-format: on

Expand All @@ -603,14 +548,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 +938,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 +978,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
Loading

0 comments on commit f30642f

Please sign in to comment.