Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed issue #2079: Add pseudo hosts xdebug://gateway and xdebug://nameserver #833

Merged
merged 2 commits into from May 4, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion config.m4
Expand Up @@ -97,7 +97,7 @@ if test "$PHP_XDEBUG" != "no"; then
XDEBUG_LIB_SOURCES="src/lib/usefulstuff.c src/lib/compat.c src/lib/crc32.c src/lib/file.c src/lib/hash.c src/lib/headers.c src/lib/lib.c src/lib/llist.c src/lib/log.c src/lib/set.c src/lib/str.c src/lib/timing.c src/lib/var.c src/lib/var_export_html.c src/lib/var_export_line.c src/lib/var_export_text.c src/lib/var_export_xml.c src/lib/xml.c"

XDEBUG_COVERAGE_SOURCES="src/coverage/branch_info.c src/coverage/code_coverage.c"
XDEBUG_DEBUGGER_SOURCES="src/debugger/com.c src/debugger/debugger.c src/debugger/handler_dbgp.c src/debugger/handlers.c"
XDEBUG_DEBUGGER_SOURCES="src/debugger/com.c src/debugger/debugger.c src/debugger/handler_dbgp.c src/debugger/handlers.c src/debugger/ip_info.c"
XDEBUG_DEVELOP_SOURCES="src/develop/develop.c src/develop/monitor.c src/develop/php_functions.c src/develop/stack.c src/develop/superglobals.c"
XDEBUG_GCSTATS_SOURCES="src/gcstats/gc_stats.c"
XDEBUG_PROFILER_SOURCES="src/profiler/profiler.c"
Expand Down
2 changes: 2 additions & 0 deletions package.xml
Expand Up @@ -166,6 +166,8 @@ Thu, Apr 08, 2021 - Xdebug 3.0.4
<file name="handlers.h" role="src" />
<file name="handler_dbgp.c" role="src" />
<file name="handler_dbgp.h" role="src" />
<file name="ip_info.c" role="src" />
<file name="ip_info.h" role="src" />
</dir>
<dir name="gcstats">
<file name="gc_stats.c" role="src" />
Expand Down
1 change: 1 addition & 0 deletions src/base/base.c
Expand Up @@ -1173,6 +1173,7 @@ int read_systemd_private_tmp_directory(char **private_tmp)

/* Clean up and return */
xdebug_arg_dtor(lines);
fclose(mountinfo_fd);
return retval;
}
#endif
Expand Down
54 changes: 53 additions & 1 deletion src/debugger/com.c
Expand Up @@ -50,6 +50,7 @@

#include "debugger_private.h"
#include "handler_dbgp.h"
#include "ip_info.h"
#include "lib/crc32.h"
#include "lib/log.h"

Expand Down Expand Up @@ -134,6 +135,46 @@ void set_keepalive_options(int fd)
}
#endif // !WIN32 && !WINNT

static char* resolve_pseudo_hosts(const char *requested_hostname)
{
#if __linux__
/* Does it start with 'xdebug://' ? */
if (strncmp(requested_hostname, "xdebug://", strlen("xdebug://")) != 0) {
return NULL;
}

/* Check for 'gateway' pseudo host */
if (strcmp(requested_hostname, "xdebug://gateway") == 0) {
char *gateway = xdebug_get_gateway_ip();

if (!gateway) {
xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "GATEWAY", "Could not find network gateway to use for 'gateway' pseudo-host.");
return NULL;
}

xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Found 'gateway' pseudo-host, with IP address '%s'.", gateway);
return gateway;
}

/* Check for 'nameserver' pseudo host */
if (strcmp(requested_hostname, "xdebug://nameserver") == 0) {
char *gateway = xdebug_get_private_nameserver();

if (!gateway) {
xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "NAMESERVER", "Could not find a private network nameserver for 'nameserver' pseudo-host.");
return NULL;
}

xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Found 'nameserver' pseudo-host, with IP address '%s'.", gateway);
return gateway;
}

xdebug_log_ex(XLOG_CHAN_DEBUG, XLOG_WARN, "UNKNOWN-PSEUDO", "Unknown pseudo-host: '%s', only 'gateway' or 'nameserver' are supported.", requested_hostname + strlen("xdebug://"));
#endif

return NULL;
}

static int xdebug_create_socket(const char *hostname, int dport, int timeout)
{
struct addrinfo hints;
Expand Down Expand Up @@ -332,7 +373,7 @@ static int xdebug_create_socket(const char *hostname, int dport, int timeout)
break;
}

/* Free the result returned by getaddrinfo */
/* Free the result returned by getaddrinfo, as well as the duplicated hostname */
freeaddrinfo(remote);

/* If we got a socket, set the option "No delay" to true (1) */
Expand Down Expand Up @@ -376,6 +417,17 @@ static void xdebug_init_normal_debugger(xdebug_str *connection_attempts)
const char *header = NULL;

if (!XINI_DBG(discover_client_host)) {
char *pseudo_hostname = resolve_pseudo_hosts(XINI_DBG(client_host));

if (pseudo_hostname) {
xdebug_str_add_fmt(connection_attempts, "%s:%ld (through xdebug.client_host/xdebug.client_port, from %s)", pseudo_hostname, XINI_DBG(client_port), XINI_DBG(client_host));
xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connecting to resolved address/port: %s:%ld.", pseudo_hostname, (long int) XINI_DBG(client_port));

XG_DBG(context).socket = xdebug_create_socket(pseudo_hostname, XINI_DBG(client_port), XINI_DBG(connect_timeout_ms));
xdfree(pseudo_hostname);
return;
}

xdebug_str_add_fmt(connection_attempts, "%s:%ld (through xdebug.client_host/xdebug.client_port)", XINI_DBG(client_host), XINI_DBG(client_port));
xdebug_log(XLOG_CHAN_DEBUG, XLOG_INFO, "Connecting to configured address/port: %s:%ld.", XINI_DBG(client_host), (long int) XINI_DBG(client_port));

Expand Down
283 changes: 283 additions & 0 deletions src/debugger/ip_info.c
@@ -0,0 +1,283 @@
/*
+----------------------------------------------------------------------+
| Xdebug |
+----------------------------------------------------------------------+
| Copyright (c) 2002-2022 Derick Rethans |
+----------------------------------------------------------------------+
| This source file is subject to version 1.01 of the Xdebug license, |
| that is bundled with this package in the file LICENSE, and is |
| available at through the world-wide-web at |
| https://xdebug.org/license.php |
| If you did not receive a copy of the Xdebug license and are unable |
| to obtain it through the world-wide-web, please send a note to |
| derick@xdebug.org so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifdef __linux__

#include "php_xdebug.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>


#define BUFFER_SIZE 4096

static char *convert_to_quad(int domain, void *buf)
{
char *ip = xdcalloc(1, INET6_ADDRSTRLEN + 1);

inet_ntop(domain, buf, ip, INET6_ADDRSTRLEN);

return ip;
}

static int get_ip(int fd, struct sockaddr_nl *sa, int domain)
{
char buf[BUFFER_SIZE];
struct nlmsghdr *nl;
struct ifaddrmsg *ifa;
struct iovec iov = { 0 };
struct msghdr msg = { 0 };
int r;

memset(buf, 0, BUFFER_SIZE);

/* Assemble the message according to the netlink protocol */
nl = (struct nlmsghdr*)buf;
nl->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
nl->nlmsg_type = RTM_GETADDR;
nl->nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;

ifa = (struct ifaddrmsg*)NLMSG_DATA(nl);
ifa->ifa_family = domain; // We only get IPv4 address here

/* Prepare struct msghdr for sending */
iov.iov_base = nl;
iov.iov_len = nl->nlmsg_len;
msg.msg_name = sa;
msg.msg_namelen = sizeof(*sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;

/* Send netlink message to kernel */
r = sendmsg(fd, &msg, 0);
return (r < 0) ? -1 : 0;
}

static int get_msg(int fd, struct sockaddr_nl *sa, void *buf, size_t len)
{
struct iovec iov;
struct msghdr msg;

iov.iov_base = buf;
iov.iov_len = len;

memset(&msg, 0, sizeof(msg));
msg.msg_name = sa;
msg.msg_namelen = sizeof(*sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

return recvmsg(fd, &msg, 0);
}

static char *parse_ifa_msg(struct ifaddrmsg *ifa, void *buf, size_t len, const char *wanted_iface)
{
char ifname[IF_NAMESIZE];
struct rtattr *rta = NULL;
int fa = ifa->ifa_family;

if_indextoname(ifa->ifa_index, ifname);

if (strcmp(ifname, wanted_iface) != 0) {
return NULL;
}

for (rta = (struct rtattr*)buf; RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
if (rta->rta_type == IFA_ADDRESS) {
return convert_to_quad(fa, RTA_DATA(rta));
}
}

return NULL;
}

static uint32_t parse_nl_msg(void *buf, size_t len, const char *iface, char **if_address)
{
struct nlmsghdr *nl = NULL;
uint32_t nlmsg_type = NLMSG_ERROR;

for (
nl = (struct nlmsghdr*)buf;
NLMSG_OK(nl, (uint32_t)len) && nl->nlmsg_type != NLMSG_DONE;
nl = NLMSG_NEXT(nl, len)
) {
nlmsg_type = nl->nlmsg_type;

if (nl->nlmsg_type == NLMSG_ERROR) {
return -1;
}

if (nl->nlmsg_type == RTM_NEWADDR) {
struct ifaddrmsg *ifa;
ifa = (struct ifaddrmsg*)NLMSG_DATA(nl);

if (*if_address == NULL) {
*if_address = parse_ifa_msg(ifa, IFA_RTA(ifa), IFA_PAYLOAD(nl), iface);
if (*if_address) {
return NLMSG_DONE;
}
}
continue;
}
}
return nlmsg_type;
}

char *xdebug_get_ip_for_interface(const char *iface)
{
char buf[BUFFER_SIZE];
int fd = 0, len = 0;
char *if_address = NULL;
uint32_t nl_msg_type = NLMSG_DONE;
struct sockaddr_nl sa;

/* Create a socket with the AF_NETLINK domain */
fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0) {
return NULL;
}

memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;

len = get_ip(fd, &sa, AF_INET); // For IPv6: use AF_INET6 instead
if (len < 0) {
return NULL;
}

do {
len = get_msg(fd, &sa, buf, BUFFER_SIZE);
if (len < 0) {
return NULL;
}

nl_msg_type = parse_nl_msg(buf, len, iface, &if_address);
} while (nl_msg_type != NLMSG_DONE && nl_msg_type != NLMSG_ERROR);

return if_address;
}

/***/

static int get_gateway_and_iface(in_addr_t *addr, char *interface)
{
long destination, gateway;
char iface[IF_NAMESIZE];
char buf[BUFFER_SIZE];
FILE *file;

memset(iface, 0, sizeof(iface));
memset(buf, 0, sizeof(buf));

file = fopen("/proc/net/route", "r");
if (!file) {
return 0;
}

while (fgets(buf, sizeof(buf), file)) {
if (sscanf(buf, "%s %lx %lx", iface, &destination, &gateway) == 3) {
if (destination == 0) { /* default */
*addr = gateway;
strcpy(interface, iface);
fclose(file);
return 1;
}
}
}

/* default route not found */
if (file) {
fclose(file);
}
return 0;
}

char *xdebug_get_gateway_ip(void)
{
in_addr_t addr = 0;
char iface[IF_NAMESIZE];

memset(iface, 0, sizeof(iface));

if (get_gateway_and_iface(&addr, iface)) {
return xdstrdup(inet_ntoa(*(struct in_addr *) &addr));
}

return NULL;
}

/***/

char *xdebug_get_private_nameserver(void)
{
res_state res = malloc(sizeof(struct __res_state));
in_addr_t ns_addr = 0;
char nameserver_buf[20];
char *nameserver = NULL;

res_ninit(res);
if (res->nscount > 0 && res->nsaddr_list[0].sin_family == AF_INET) {
ns_addr = res->nsaddr_list[0].sin_addr.s_addr;
/* Only allow private networks */
if (
((ns_addr & 0x000000ff) == 0x0000000a) || // 10.x.x.x/24
((ns_addr & 0x0000f0ff) == 0x000010ac) || // 172.16.x.x/20
((ns_addr & 0x0000ffff) == 0x0000a8c0) || // 192.168.x.x/16
((ns_addr & 0x000000ff) == 0x0000007f) // 127.x.x.x/8
)
{
snprintf(
nameserver_buf, 16, "%d.%d.%d.%d",
(ns_addr & 0xff),
(ns_addr & 0xff00) >> 8,
(ns_addr & 0xff0000) >> 16,
(ns_addr & 0xff000000) >> 24
);
nameserver = xdstrdup(nameserver_buf);
}
}
res_nclose(res);
free(res);

return nameserver;
}


# if 0
int main(int argc, char *argv[])
{
char *gateway_address = xdebug_get_gateway_ip();
char *ip_address = xdebug_get_ip_for_interface(argv[1]);

printf("Gateway: %s\n", gateway_address);
printf("IP: %s\n", ip_address);

free(ip_address);
free(gateway_address);
}
# endif // int main

#endif // __linux__