This commit is contained in:
Gerard Ziemski 2017-09-01 13:03:16 +00:00
commit 407a3044c3
6 changed files with 547 additions and 54 deletions

View File

@ -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 */
};

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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 *);

View 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);
}
}
}