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
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
Loading