Merge
This commit is contained in:
commit
407a3044c3
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,7 +33,8 @@
|
||||
#include "jni.h"
|
||||
|
||||
enum {
|
||||
JDWPTRANSPORT_VERSION_1_0 = 0x00010000
|
||||
JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
|
||||
JDWPTRANSPORT_VERSION_1_1 = 0x00010001
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -142,6 +143,13 @@ typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
|
||||
jint version,
|
||||
jdwpTransportEnv** env);
|
||||
|
||||
/*
|
||||
* JDWP transport configuration from the agent.
|
||||
*/
|
||||
typedef struct jdwpTransportConfiguration {
|
||||
/* Field added in JDWPTRANSPORT_VERSION_1_1: */
|
||||
const char* allowed_peers; /* Peers allowed for connection */
|
||||
} jdwpTransportConfiguration;
|
||||
|
||||
|
||||
/* Function Interface */
|
||||
@ -191,6 +199,9 @@ struct jdwpTransportNativeInterface_ {
|
||||
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
|
||||
char** error);
|
||||
|
||||
/* 12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
|
||||
jdwpTransportConfiguration *config);
|
||||
};
|
||||
|
||||
|
||||
@ -248,6 +259,10 @@ struct _jdwpTransportEnv {
|
||||
return functions->GetLastError(this, error);
|
||||
}
|
||||
|
||||
/* SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
|
||||
jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
|
||||
return functions->SetTransportConfiguration(this, config);
|
||||
}
|
||||
|
||||
#endif /* __cplusplus */
|
||||
};
|
||||
|
@ -34,6 +34,9 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -73,6 +76,19 @@ static jdwpTransportEnv single_env = (jdwpTransportEnv)&interface;
|
||||
static jint recv_fully(int, char *, int);
|
||||
static jint send_fully(int, char *, int);
|
||||
|
||||
/* version >= JDWPTRANSPORT_VERSION_1_1 */
|
||||
typedef struct {
|
||||
uint32_t subnet;
|
||||
uint32_t netmask;
|
||||
} AllowedPeerInfo;
|
||||
|
||||
#define STR(x) #x
|
||||
#define MAX_PEER_ENTRIES 32
|
||||
#define MAX_PEERS_STR STR(MAX_PEER_ENTRIES)
|
||||
static AllowedPeerInfo _peers[MAX_PEER_ENTRIES];
|
||||
static int _peers_cnt = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Record the last error for this thread.
|
||||
*/
|
||||
@ -260,7 +276,7 @@ parseAddress(const char *address, struct sockaddr_in *sa) {
|
||||
char *colon;
|
||||
int port;
|
||||
|
||||
memset((void *)sa,0,sizeof(struct sockaddr_in));
|
||||
memset((void *)sa, 0, sizeof(struct sockaddr_in));
|
||||
sa->sin_family = AF_INET;
|
||||
|
||||
/* check for host:port or port */
|
||||
@ -274,7 +290,7 @@ parseAddress(const char *address, struct sockaddr_in *sa) {
|
||||
if (colon == NULL) {
|
||||
// bind to localhost only if no address specified
|
||||
sa->sin_addr.s_addr = getLocalHostAddress();
|
||||
} else if (strncmp(address,"localhost:",10) == 0) {
|
||||
} else if (strncmp(address, "localhost:", 10) == 0) {
|
||||
// optimize for common case
|
||||
sa->sin_addr.s_addr = getLocalHostAddress();
|
||||
} else if (*address == '*' && *(address+1) == ':') {
|
||||
@ -286,7 +302,7 @@ parseAddress(const char *address, struct sockaddr_in *sa) {
|
||||
char *hostname;
|
||||
uint32_t addr;
|
||||
|
||||
buf = (*callback->alloc)((int)strlen(address)+1);
|
||||
buf = (*callback->alloc)((int)strlen(address) + 1);
|
||||
if (buf == NULL) {
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_OUT_OF_MEMORY, "out of memory");
|
||||
}
|
||||
@ -320,6 +336,131 @@ parseAddress(const char *address, struct sockaddr_in *sa) {
|
||||
return JDWPTRANSPORT_ERROR_NONE;
|
||||
}
|
||||
|
||||
static const char *
|
||||
ip_s2u(const char *instr, uint32_t *ip) {
|
||||
// Convert string representation of ip to integer
|
||||
// in network byte order (big-endian)
|
||||
char t[4] = { 0, 0, 0, 0 };
|
||||
const char *s = instr;
|
||||
int i = 0;
|
||||
|
||||
while (1) {
|
||||
if (*s == '.') {
|
||||
++i;
|
||||
++s;
|
||||
continue;
|
||||
}
|
||||
if (*s == 0 || *s == '+' || *s == '/') {
|
||||
break;
|
||||
}
|
||||
if (*s < '0' || *s > '9') {
|
||||
return instr;
|
||||
}
|
||||
t[i] = (t[i] * 10) + (*s - '0');
|
||||
++s;
|
||||
}
|
||||
|
||||
*ip = *(uint32_t*)(t);
|
||||
return s;
|
||||
}
|
||||
|
||||
static const char *
|
||||
mask_s2u(const char *instr, uint32_t *mask) {
|
||||
// Convert the number of bits to a netmask
|
||||
// in network byte order (big-endian)
|
||||
unsigned char m = 0;
|
||||
const char *s = instr;
|
||||
|
||||
while (1) {
|
||||
if (*s == 0 || *s == '+') {
|
||||
break;
|
||||
}
|
||||
if (*s < '0' || *s > '9') {
|
||||
return instr;
|
||||
}
|
||||
m = (m * 10) + (*s - '0');
|
||||
++s;
|
||||
}
|
||||
|
||||
if (m == 0 || m > 32) {
|
||||
// Drop invalid input
|
||||
return instr;
|
||||
}
|
||||
|
||||
*mask = htonl(-1 << (32 - m));
|
||||
return s;
|
||||
}
|
||||
|
||||
static int
|
||||
ip_in_subnet(uint32_t subnet, uint32_t mask, uint32_t ipaddr) {
|
||||
return (ipaddr & mask) == subnet;
|
||||
}
|
||||
|
||||
static jdwpTransportError
|
||||
parseAllowedPeers(const char *allowed_peers) {
|
||||
// Build a list of allowed peers from char string
|
||||
// of format 192.168.0.10+192.168.0.0/24
|
||||
const char *s = NULL;
|
||||
const char *p = allowed_peers;
|
||||
uint32_t ip = 0;
|
||||
uint32_t mask = 0xFFFFFFFF;
|
||||
|
||||
while (1) {
|
||||
s = ip_s2u(p, &ip);
|
||||
if (s == p) {
|
||||
_peers_cnt = 0;
|
||||
fprintf(stderr, "Error in allow option: '%s'\n", s);
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"invalid IP address in allow option");
|
||||
}
|
||||
|
||||
if (*s == '/') {
|
||||
// netmask specified
|
||||
s = mask_s2u(s + 1, &mask);
|
||||
if (*(s - 1) == '/') {
|
||||
// Input is not consumed, something bad happened
|
||||
_peers_cnt = 0;
|
||||
fprintf(stderr, "Error in allow option: '%s'\n", s);
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"invalid netmask in allow option");
|
||||
}
|
||||
} else {
|
||||
// reset netmask
|
||||
mask = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if (*s == '+' || *s == 0) {
|
||||
if (_peers_cnt >= MAX_PEER_ENTRIES) {
|
||||
fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"exceeded max number of allowed peers: " MAX_PEERS_STR);
|
||||
}
|
||||
_peers[_peers_cnt].subnet = ip;
|
||||
_peers[_peers_cnt].netmask = mask;
|
||||
_peers_cnt++;
|
||||
if (*s == 0) {
|
||||
// end of options
|
||||
break;
|
||||
}
|
||||
// advance to next IP block
|
||||
p = s + 1;
|
||||
}
|
||||
}
|
||||
return JDWPTRANSPORT_ERROR_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
isPeerAllowed(struct sockaddr_in *peer) {
|
||||
int i;
|
||||
for (i = 0; i < _peers_cnt; ++i) {
|
||||
int peer_ip = peer->sin_addr.s_addr;
|
||||
if (ip_in_subnet(_peers[i].subnet, _peers[i].netmask, peer_ip)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static jdwpTransportError JNICALL
|
||||
socketTransport_getCapabilities(jdwpTransportEnv* env,
|
||||
@ -412,7 +553,7 @@ static jdwpTransportError JNICALL
|
||||
socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handshakeTimeout)
|
||||
{
|
||||
socklen_t socketLen;
|
||||
int err;
|
||||
int err = JDWPTRANSPORT_ERROR_NONE;
|
||||
struct sockaddr_in socket;
|
||||
jlong startTime = (jlong)0;
|
||||
|
||||
@ -474,14 +615,34 @@ socketTransport_accept(jdwpTransportEnv* env, jlong acceptTimeout, jlong handsha
|
||||
return JDWPTRANSPORT_ERROR_IO_ERROR;
|
||||
}
|
||||
|
||||
/* handshake with the debugger */
|
||||
err = handshake(socketFD, handshakeTimeout);
|
||||
/*
|
||||
* version >= JDWPTRANSPORT_VERSION_1_1:
|
||||
* Verify that peer is allowed to connect.
|
||||
*/
|
||||
if (_peers_cnt > 0) {
|
||||
if (!isPeerAllowed(&socket)) {
|
||||
char ebuf[64] = { 0 };
|
||||
char buf[INET_ADDRSTRLEN] = { 0 };
|
||||
const char* addr_str = inet_ntop(AF_INET, &(socket.sin_addr), buf, INET_ADDRSTRLEN);
|
||||
sprintf(ebuf, "ERROR: Peer not allowed to connect: %s\n",
|
||||
(addr_str == NULL) ? "<bad address>" : addr_str);
|
||||
dbgsysSocketClose(socketFD);
|
||||
socketFD = -1;
|
||||
err = JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT;
|
||||
setLastError(err, ebuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (socketFD > 0) {
|
||||
/* handshake with the debugger */
|
||||
err = handshake(socketFD, handshakeTimeout);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the handshake fails then close the connection. If there if an accept
|
||||
* timeout then we must adjust the timeout for the next poll.
|
||||
*/
|
||||
if (err) {
|
||||
if (err != JDWPTRANSPORT_ERROR_NONE) {
|
||||
fprintf(stderr, "Debugger failed to attach: %s\n", getLastError());
|
||||
dbgsysSocketClose(socketFD);
|
||||
socketFD = -1;
|
||||
@ -743,20 +904,20 @@ socketTransport_readPacket(jdwpTransportEnv* env, jdwpPacket* packet) {
|
||||
packet->type.cmd.len = length;
|
||||
|
||||
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.id),sizeof(jint));
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.id), sizeof(jint));
|
||||
if (n < (int)sizeof(jint)) {
|
||||
RETURN_RECV_ERROR(n);
|
||||
}
|
||||
|
||||
packet->type.cmd.id = (jint)dbgsysNetworkToHostLong(packet->type.cmd.id);
|
||||
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags),sizeof(jbyte));
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.flags), sizeof(jbyte));
|
||||
if (n < (int)sizeof(jbyte)) {
|
||||
RETURN_RECV_ERROR(n);
|
||||
}
|
||||
|
||||
if (packet->type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode),sizeof(jbyte));
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.reply.errorCode), sizeof(jbyte));
|
||||
if (n < (int)sizeof(jshort)) {
|
||||
RETURN_RECV_ERROR(n);
|
||||
}
|
||||
@ -765,12 +926,12 @@ socketTransport_readPacket(jdwpTransportEnv* env, jdwpPacket* packet) {
|
||||
|
||||
|
||||
} else {
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet),sizeof(jbyte));
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmdSet), sizeof(jbyte));
|
||||
if (n < (int)sizeof(jbyte)) {
|
||||
RETURN_RECV_ERROR(n);
|
||||
}
|
||||
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd),sizeof(jbyte));
|
||||
n = recv_fully(socketFD,(char *)&(packet->type.cmd.cmd), sizeof(jbyte));
|
||||
if (n < (int)sizeof(jbyte)) {
|
||||
RETURN_RECV_ERROR(n);
|
||||
}
|
||||
@ -814,11 +975,44 @@ socketTransport_getLastError(jdwpTransportEnv* env, char** msgP) {
|
||||
return JDWPTRANSPORT_ERROR_NONE;
|
||||
}
|
||||
|
||||
static jdwpTransportError JNICALL
|
||||
socketTransport_setConfiguration(jdwpTransportEnv* env, jdwpTransportConfiguration* cfg) {
|
||||
const char* allowed_peers = NULL;
|
||||
|
||||
if (cfg == NULL) {
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"NULL pointer to transport configuration is invalid");
|
||||
}
|
||||
allowed_peers = cfg->allowed_peers;
|
||||
_peers_cnt = 0;
|
||||
if (allowed_peers != NULL) {
|
||||
size_t len = strlen(allowed_peers);
|
||||
if (len == 0) { /* Impossible: parseOptions() would reject it */
|
||||
fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"allow option should not be empty");
|
||||
} else if (*allowed_peers == '*') {
|
||||
if (len != 1) {
|
||||
fprintf(stderr, "Error in allow option: '%s'\n", allowed_peers);
|
||||
RETURN_ERROR(JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT,
|
||||
"allow option '*' cannot be expanded");
|
||||
}
|
||||
} else {
|
||||
int err = parseAllowedPeers(allowed_peers);
|
||||
if (err != JDWPTRANSPORT_ERROR_NONE) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
return JDWPTRANSPORT_ERROR_NONE;
|
||||
}
|
||||
|
||||
jint JNICALL
|
||||
jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
|
||||
jint version, jdwpTransportEnv** result)
|
||||
jint version, jdwpTransportEnv** env)
|
||||
{
|
||||
if (version != JDWPTRANSPORT_VERSION_1_0) {
|
||||
if (version < JDWPTRANSPORT_VERSION_1_0 ||
|
||||
version > JDWPTRANSPORT_VERSION_1_1) {
|
||||
return JNI_EVERSION;
|
||||
}
|
||||
if (initialized) {
|
||||
@ -842,7 +1036,10 @@ jdwpTransport_OnLoad(JavaVM *vm, jdwpTransportCallback* cbTablePtr,
|
||||
interface.ReadPacket = &socketTransport_readPacket;
|
||||
interface.WritePacket = &socketTransport_writePacket;
|
||||
interface.GetLastError = &socketTransport_getLastError;
|
||||
*result = &single_env;
|
||||
if (version >= JDWPTRANSPORT_VERSION_1_1) {
|
||||
interface.SetTransportConfiguration = &socketTransport_setConfiguration;
|
||||
}
|
||||
*env = &single_env;
|
||||
|
||||
/* initialized TLS */
|
||||
tlsIndex = dbgsysTlsAlloc();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -89,6 +89,7 @@ typedef struct TransportSpec {
|
||||
char *name;
|
||||
char *address;
|
||||
long timeout;
|
||||
char *allow;
|
||||
} TransportSpec;
|
||||
|
||||
/*
|
||||
@ -564,7 +565,8 @@ startTransport(void *item, void *arg)
|
||||
|
||||
LOG_MISC(("Begin startTransport"));
|
||||
serror = transport_startTransport(enumArg->isServer, transport->name,
|
||||
transport->address, transport->timeout);
|
||||
transport->address, transport->timeout,
|
||||
transport->allow);
|
||||
if (serror != JDWP_ERROR(NONE)) {
|
||||
ERROR_MESSAGE(("JDWP Transport %s failed to initialize, %s(%d)",
|
||||
transport->name, jdwpErrorText(serror), serror));
|
||||
@ -1060,7 +1062,6 @@ parseOptions(char *options)
|
||||
if (transports == NULL) {
|
||||
EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"transports");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
current = names;
|
||||
@ -1080,6 +1081,9 @@ parseOptions(char *options)
|
||||
goto syntax_error;
|
||||
}
|
||||
currentTransport->name = current;
|
||||
currentTransport->address = NULL;
|
||||
currentTransport->allow = NULL;
|
||||
currentTransport->timeout = 0L;
|
||||
current += strlen(current) + 1;
|
||||
} else if (strcmp(buf, "address") == 0) {
|
||||
if (currentTransport == NULL) {
|
||||
@ -1092,7 +1096,18 @@ parseOptions(char *options)
|
||||
}
|
||||
currentTransport->address = current;
|
||||
current += strlen(current) + 1;
|
||||
} else if (strcmp(buf, "timeout") == 0) {
|
||||
} else if (strcmp(buf, "allow") == 0) {
|
||||
if (currentTransport == NULL) {
|
||||
errmsg = "allow specified without transport";
|
||||
goto bad_option_with_errmsg;
|
||||
}
|
||||
/*LINTED*/
|
||||
if (!get_tok(&str, current, (int)(end - current), ',')) {
|
||||
goto syntax_error;
|
||||
}
|
||||
currentTransport->allow = current;
|
||||
current += strlen(current) + 1;
|
||||
} else if (strcmp(buf, "timeout") == 0) {
|
||||
if (currentTransport == NULL) {
|
||||
errmsg = "timeout specified without transport";
|
||||
goto bad_option_with_errmsg;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,7 +29,9 @@
|
||||
#include "debugLoop.h"
|
||||
#include "sys.h"
|
||||
|
||||
static jdwpTransportEnv *transport;
|
||||
static jdwpTransportEnv *transport = NULL;
|
||||
static unsigned transportVersion = JDWPTRANSPORT_VERSION_1_0;
|
||||
|
||||
static jrawMonitorID listenerLock;
|
||||
static jrawMonitorID sendLock;
|
||||
|
||||
@ -41,6 +43,8 @@ typedef struct TransportInfo {
|
||||
jdwpTransportEnv *transport;
|
||||
char *address;
|
||||
long timeout;
|
||||
char *allowed_peers;
|
||||
unsigned transportVersion;
|
||||
} TransportInfo;
|
||||
|
||||
static struct jdwpTransportCallback callback = {jvmtiAllocate, jvmtiDeallocate};
|
||||
@ -135,7 +139,7 @@ loadTransportLibrary(const char *libdir, const char *name)
|
||||
* JDK 1.2 javai.c v1.61
|
||||
*/
|
||||
static jdwpError
|
||||
loadTransport(const char *name, jdwpTransportEnv **transportPtr)
|
||||
loadTransport(const char *name, TransportInfo *info)
|
||||
{
|
||||
JNIEnv *env;
|
||||
jdwpTransport_OnLoad_t onLoad;
|
||||
@ -147,6 +151,10 @@ loadTransport(const char *name, jdwpTransportEnv **transportPtr)
|
||||
ERROR_MESSAGE(("library name is empty"));
|
||||
return JDWP_ERROR(TRANSPORT_LOAD);
|
||||
}
|
||||
if (info == NULL) {
|
||||
ERROR_MESSAGE(("internal error: info should not be NULL"));
|
||||
return JDWP_ERROR(TRANSPORT_LOAD);
|
||||
}
|
||||
|
||||
/* First, look in sun.boot.library.path. This should find the standard
|
||||
* dt_socket and dt_shmem transport libraries, or any library
|
||||
@ -192,22 +200,34 @@ loadTransport(const char *name, jdwpTransportEnv **transportPtr)
|
||||
|
||||
/* Get transport interface */
|
||||
env = getEnv();
|
||||
if ( env != NULL ) {
|
||||
jdwpTransportEnv *t;
|
||||
JavaVM *jvm;
|
||||
jint ver;
|
||||
if (env != NULL) {
|
||||
jdwpTransportEnv *t = NULL;
|
||||
JavaVM *jvm = NULL;
|
||||
jint rc;
|
||||
size_t i;
|
||||
/* If a new version is added here, update 'case JNI_EVERSION' below. */
|
||||
jint supported_versions[2] = {JDWPTRANSPORT_VERSION_1_1, JDWPTRANSPORT_VERSION_1_0};
|
||||
|
||||
JNI_FUNC_PTR(env,GetJavaVM)(env, &jvm);
|
||||
ver = (*onLoad)(jvm, &callback, JDWPTRANSPORT_VERSION_1_0, &t);
|
||||
if (ver != JNI_OK) {
|
||||
switch (ver) {
|
||||
|
||||
/* Try version 1.1 first, fallback to 1.0 on error */
|
||||
for (i = 0; i < sizeof(supported_versions); ++i) {
|
||||
rc = (*onLoad)(jvm, &callback, supported_versions[i], &t);
|
||||
if (rc != JNI_EVERSION) {
|
||||
info->transportVersion = supported_versions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc != JNI_OK) {
|
||||
switch (rc) {
|
||||
case JNI_ENOMEM :
|
||||
ERROR_MESSAGE(("insufficient memory to complete initialization"));
|
||||
break;
|
||||
|
||||
case JNI_EVERSION :
|
||||
ERROR_MESSAGE(("transport doesn't recognize version %x",
|
||||
JDWPTRANSPORT_VERSION_1_0));
|
||||
ERROR_MESSAGE(("transport doesn't recognize all supported versions: "
|
||||
"{ 1_1, 1_0 }"));
|
||||
break;
|
||||
|
||||
case JNI_EEXIST :
|
||||
@ -215,13 +235,19 @@ loadTransport(const char *name, jdwpTransportEnv **transportPtr)
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_MESSAGE(("unrecognized error %d from transport", ver));
|
||||
ERROR_MESSAGE(("unrecognized error %d from transport", rc));
|
||||
break;
|
||||
}
|
||||
|
||||
return JDWP_ERROR(TRANSPORT_INIT);
|
||||
}
|
||||
*transportPtr = t;
|
||||
|
||||
/* Store transport version to global variable to be able to
|
||||
* set correct transport version for subsequent connect,
|
||||
* even if info is already deallocated.
|
||||
*/
|
||||
transportVersion = info->transportVersion;
|
||||
info->transport = t;
|
||||
} else {
|
||||
return JDWP_ERROR(TRANSPORT_LOAD);
|
||||
}
|
||||
@ -314,7 +340,6 @@ acceptThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
|
||||
|
||||
info = (TransportInfo*)(void*)arg;
|
||||
t = info->transport;
|
||||
|
||||
rc = (*t)->Accept(t, info->timeout, 0);
|
||||
|
||||
/* System property no longer needed */
|
||||
@ -339,8 +364,10 @@ acceptThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
|
||||
static void JNICALL
|
||||
attachThread(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
|
||||
{
|
||||
TransportInfo *info = (TransportInfo*)(void*)arg;
|
||||
|
||||
LOG_MISC(("Begin attach thread"));
|
||||
connectionInitiated((jdwpTransportEnv *)(void*)arg);
|
||||
connectionInitiated(info->transport);
|
||||
LOG_MISC(("End attach thread"));
|
||||
}
|
||||
|
||||
@ -418,13 +445,26 @@ launch(char *command, char *name, char *address)
|
||||
|
||||
jdwpError
|
||||
transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
long timeout)
|
||||
long timeout, char *allowed_peers)
|
||||
{
|
||||
jvmtiStartFunction func;
|
||||
jdwpTransportEnv *trans;
|
||||
char threadName[MAXPATHLEN + 100];
|
||||
jint err;
|
||||
jdwpError serror;
|
||||
jdwpTransportConfiguration cfg = {0};
|
||||
TransportInfo *info;
|
||||
jdwpTransportEnv *trans;
|
||||
|
||||
info = jvmtiAllocate(sizeof(*info));
|
||||
if (info == NULL) {
|
||||
return JDWP_ERROR(OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
info->transport = transport;
|
||||
info->transportVersion = transportVersion;
|
||||
info->name = NULL;
|
||||
info->address = NULL;
|
||||
info->allowed_peers = NULL;
|
||||
|
||||
/*
|
||||
* If the transport is already loaded then use it
|
||||
@ -434,28 +474,24 @@ transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
* That probably means we have a bag a transport environments
|
||||
* to correspond to the transports bag.
|
||||
*/
|
||||
if (transport != NULL) {
|
||||
trans = transport;
|
||||
} else {
|
||||
serror = loadTransport(name, &trans);
|
||||
if (info->transport == NULL) {
|
||||
serror = loadTransport(name, info);
|
||||
if (serror != JDWP_ERROR(NONE)) {
|
||||
jvmtiDeallocate(info);
|
||||
return serror;
|
||||
}
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
// Cache the value
|
||||
trans = info->transport;
|
||||
|
||||
if (isServer) {
|
||||
char *retAddress;
|
||||
char *launchCommand;
|
||||
TransportInfo *info;
|
||||
jvmtiError error;
|
||||
int len;
|
||||
char* prop_value;
|
||||
|
||||
info = jvmtiAllocate(sizeof(*info));
|
||||
if (info == NULL) {
|
||||
return JDWP_ERROR(OUT_OF_MEMORY);
|
||||
}
|
||||
info->timeout = timeout;
|
||||
|
||||
info->name = jvmtiAllocate((int)strlen(name)+1);
|
||||
@ -465,7 +501,6 @@ transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
}
|
||||
(void)strcpy(info->name, name);
|
||||
|
||||
info->address = NULL;
|
||||
if (address != NULL) {
|
||||
info->address = jvmtiAllocate((int)strlen(address)+1);
|
||||
if (info->address == NULL) {
|
||||
@ -475,7 +510,32 @@ transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
(void)strcpy(info->address, address);
|
||||
}
|
||||
|
||||
info->transport = trans;
|
||||
if (info->transportVersion == JDWPTRANSPORT_VERSION_1_0) {
|
||||
if (allowed_peers != NULL) {
|
||||
ERROR_MESSAGE(("Allow parameter is specified but transport doesn't support it"));
|
||||
serror = JDWP_ERROR(TRANSPORT_INIT);
|
||||
goto handleError;
|
||||
}
|
||||
} else {
|
||||
/* Memory is allocated only for transport versions > 1.0
|
||||
* as the version 1.0 does not support the 'allow' option.
|
||||
*/
|
||||
if (allowed_peers != NULL) {
|
||||
info->allowed_peers = jvmtiAllocate((int)strlen(allowed_peers) + 1);
|
||||
if (info->allowed_peers == NULL) {
|
||||
serror = JDWP_ERROR(OUT_OF_MEMORY);
|
||||
goto handleError;
|
||||
}
|
||||
(void)strcpy(info->allowed_peers, allowed_peers);
|
||||
}
|
||||
cfg.allowed_peers = info->allowed_peers;
|
||||
err = (*trans)->SetTransportConfiguration(trans, &cfg);
|
||||
if (err != JDWPTRANSPORT_ERROR_NONE) {
|
||||
printLastError(trans, err);
|
||||
serror = JDWP_ERROR(TRANSPORT_INIT);
|
||||
goto handleError;
|
||||
}
|
||||
}
|
||||
|
||||
err = (*trans)->StartListening(trans, address, &retAddress);
|
||||
if (err != JDWPTRANSPORT_ERROR_NONE) {
|
||||
@ -527,6 +587,7 @@ transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
handleError:
|
||||
jvmtiDeallocate(info->name);
|
||||
jvmtiDeallocate(info->address);
|
||||
jvmtiDeallocate(info->allowed_peers);
|
||||
jvmtiDeallocate(info);
|
||||
} else {
|
||||
/*
|
||||
@ -543,6 +604,10 @@ handleError:
|
||||
if (err != JDWPTRANSPORT_ERROR_NONE) {
|
||||
printLastError(trans, err);
|
||||
serror = JDWP_ERROR(TRANSPORT_INIT);
|
||||
/* The name, address and allowed_peers fields in 'info'
|
||||
* are not allocated in the non-server case so
|
||||
* they do not need to be freed. */
|
||||
jvmtiDeallocate(info);
|
||||
return serror;
|
||||
}
|
||||
|
||||
@ -553,7 +618,7 @@ handleError:
|
||||
(void)strcat(threadName, name);
|
||||
|
||||
func = &attachThread;
|
||||
err = spawnNewThread(func, (void*)trans, threadName);
|
||||
err = spawnNewThread(func, (void*)info, threadName);
|
||||
serror = map2jdwpError(err);
|
||||
}
|
||||
return serror;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,7 +30,8 @@
|
||||
|
||||
void transport_initialize(void);
|
||||
void transport_reset(void);
|
||||
jdwpError transport_startTransport(jboolean isServer, char *name, char *address, long timeout);
|
||||
jdwpError transport_startTransport(jboolean isServer, char *name, char *address,
|
||||
long timeout, char *allowed_peers);
|
||||
|
||||
jint transport_receivePacket(jdwpPacket *);
|
||||
jint transport_sendPacket(jdwpPacket *);
|
||||
|
200
jdk/test/com/sun/jdi/BasicJDWPConnectionTest.java
Normal file
200
jdk/test/com/sun/jdi/BasicJDWPConnectionTest.java
Normal file
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Smoke test for JDWP hardening
|
||||
* @library /lib/testlibrary
|
||||
* @library /test/lib
|
||||
* @run driver BasicJDWPConnectionTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
|
||||
import jdk.test.lib.apps.LingeredApp;
|
||||
import jdk.testlibrary.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class BasicJDWPConnectionTest {
|
||||
|
||||
public static int handshake(int port) throws IOException {
|
||||
// Connect to the debuggee and handshake
|
||||
int res = -1;
|
||||
Socket s = null;
|
||||
try {
|
||||
s = new Socket("localhost", port);
|
||||
s.getOutputStream().write("JDWP-Handshake".getBytes("UTF-8"));
|
||||
byte[] buffer = new byte[24];
|
||||
res = s.getInputStream().read(buffer);
|
||||
}
|
||||
catch (SocketException ex) {
|
||||
// pass
|
||||
} finally {
|
||||
if (s != null) {
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static ArrayList<String> prepareCmd(int port, String allowOpt) {
|
||||
String address = "*:" + String.valueOf(port);
|
||||
ArrayList<String> cmd = new ArrayList<>();
|
||||
|
||||
String jdwpArgs = "-agentlib:jdwp=transport=dt_socket,server=y," +
|
||||
"suspend=n,address=" + address + allowOpt;
|
||||
cmd.add(jdwpArgs);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
public static void positiveTest(String testName, String allowOpt)
|
||||
throws InterruptedException, IOException {
|
||||
System.err.println("\nStarting " + testName);
|
||||
int port = Utils.getFreePort();
|
||||
ArrayList<String> cmd = prepareCmd(port, allowOpt);
|
||||
|
||||
LingeredApp a = LingeredApp.startApp(cmd);
|
||||
int res = handshake(port);
|
||||
a.stopApp();
|
||||
if (res < 0) {
|
||||
throw new RuntimeException(testName + " FAILED");
|
||||
}
|
||||
System.err.println(testName + " PASSED");
|
||||
}
|
||||
|
||||
public static void negativeTest(String testName, String allowOpt)
|
||||
throws InterruptedException, IOException {
|
||||
System.err.println("\nStarting " + testName);
|
||||
int port = Utils.getFreePort();
|
||||
ArrayList<String> cmd = prepareCmd(port, allowOpt);
|
||||
|
||||
LingeredApp a = LingeredApp.startApp(cmd);
|
||||
int res = handshake(port);
|
||||
a.stopApp();
|
||||
if (res > 0) {
|
||||
System.err.println(testName + ": res=" + res);
|
||||
throw new RuntimeException(testName + " FAILED");
|
||||
}
|
||||
System.err.println(testName + ": returned a negative code as expected: " + res);
|
||||
System.err.println(testName + " PASSED");
|
||||
}
|
||||
|
||||
public static void badAllowOptionTest(String testName, String allowOpt)
|
||||
throws InterruptedException, IOException {
|
||||
System.err.println("\nStarting " + testName);
|
||||
int port = Utils.getFreePort();
|
||||
ArrayList<String> cmd = prepareCmd(port, allowOpt);
|
||||
|
||||
try {
|
||||
LingeredApp a = LingeredApp.startApp(cmd);
|
||||
} catch (IOException ex) {
|
||||
System.err.println(testName + ": caught expected IOException");
|
||||
System.err.println(testName + " PASSED");
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException(testName + " FAILED");
|
||||
}
|
||||
|
||||
public static void DefaultTest() throws InterruptedException, IOException {
|
||||
// No allow option is the same as the allow option ',allow=*' is passed
|
||||
String allowOpt = "";
|
||||
positiveTest("DefaultTest", allowOpt);
|
||||
}
|
||||
|
||||
static void ExplicitDefaultTest() throws InterruptedException, IOException {
|
||||
// Explicit permission for connections from everywhere
|
||||
String allowOpt = ",allow=*";
|
||||
positiveTest("ExplicitDefaultTest" ,allowOpt);
|
||||
}
|
||||
|
||||
public static void AllowTest() throws InterruptedException, IOException {
|
||||
String allowOpt = ",allow=127.0.0.1";
|
||||
positiveTest("AllowTest", allowOpt);
|
||||
}
|
||||
|
||||
public static void MultiAllowTest() throws InterruptedException, IOException {
|
||||
String allowOpt = ",allow=127.0.0.1+10.0.0.0/8+172.16.0.0/12+192.168.0.0/24";
|
||||
positiveTest("MultiAllowTest", allowOpt);
|
||||
}
|
||||
|
||||
public static void DenyTest() throws InterruptedException, IOException {
|
||||
// Bad allow address
|
||||
String allowOpt = ",allow=0.0.0.0";
|
||||
negativeTest("DenyTest", allowOpt);
|
||||
}
|
||||
|
||||
public static void MultiDenyTest() throws InterruptedException, IOException {
|
||||
// Wrong separator ';' is used for allow option
|
||||
String allowOpt = ",allow=127.0.0.1;192.168.0.0/24";
|
||||
badAllowOptionTest("MultiDenyTest", allowOpt);
|
||||
}
|
||||
|
||||
public static void EmptyAllowOptionTest() throws InterruptedException, IOException {
|
||||
// Empty allow option
|
||||
String allowOpt = ",allow=";
|
||||
badAllowOptionTest("EmptyAllowOptionTest", allowOpt);
|
||||
}
|
||||
|
||||
public static void ExplicitMultiDefault1Test() throws InterruptedException, IOException {
|
||||
// Bad mix of allow option '*' with address value
|
||||
String allowOpt = ",allow=*+allow=127.0.0.1";
|
||||
badAllowOptionTest("ExplicitMultiDefault1Test", allowOpt);
|
||||
}
|
||||
|
||||
public static void ExplicitMultiDefault2Test() throws InterruptedException, IOException {
|
||||
// Bad mix of allow address value with '*'
|
||||
String allowOpt = ",allow=allow=127.0.0.1+*";
|
||||
badAllowOptionTest("ExplicitMultiDefault2Test", allowOpt);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
DefaultTest();
|
||||
ExplicitDefaultTest();
|
||||
AllowTest();
|
||||
MultiAllowTest();
|
||||
DenyTest();
|
||||
MultiDenyTest();
|
||||
EmptyAllowOptionTest();
|
||||
ExplicitMultiDefault1Test();
|
||||
ExplicitMultiDefault2Test();
|
||||
System.err.println("\nTest PASSED");
|
||||
} catch (InterruptedException ex) {
|
||||
System.err.println("\nTest ERROR, getFreePort");
|
||||
ex.printStackTrace();
|
||||
System.exit(3);
|
||||
} catch (IOException ex) {
|
||||
System.err.println("\nTest ERROR");
|
||||
ex.printStackTrace();
|
||||
System.exit(3);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user