JDK-5083594 : Different Behaviors parsing hostname with '_' between in 1.3.1 and 1.4.X/1.5
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.rmi
  • Affected Version: 5.0
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2003
  • CPU: x86
  • Submitted: 2004-08-06
  • Updated: 2005-09-20
  • Resolved: 2005-09-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 betaFixed
Related Reports
Relates :  
Relates :  
Description
In source code of Naming#parseURL() in 1.3.1, to parse URL String is hard coded.
On the other hand, to parse URL process in 1.4.X/1.5 is done 
by getHost in java.net.URI..

Please assume the URL includes a hostname which has '_'(underscore).
  ex.
    http://appsrv_k01:12200/

To parse URL process in 1.3.1 doesn't check whether the String includes  
'_'  or not, which is, that considers '_' no problem.
Then hostname is extracted as specified in URL String
(In the above example, "appsrv_k01" will be extracted.)


=== ./src/share/classes/java/rmi/Naming.java (1.3.1_12) ===

213     private static ParsedNamingURL parseURL(String url)
214         throws MalformedURLException
215     {
216         ParsedNamingURL parsed = new ParsedNamingURL();
217         int startFile = -1;
218
219         // remove the approved protocol
220         if (url.startsWith("rmi:")) {
221             url = url.substring(4);
222         }
223
224         // Anchors (i.e. '#') are meaningless in rmi URLs - disallow them
225         if (url.indexOf('#') >= 0) {
226             throw new MalformedURLException
227                 ("Invalid character, '#', in URL: " + url);
228         }
229
230         // No protocol must remain
231         int checkProtocol = url.indexOf(':');
232         if (checkProtocol >= 0 && (checkProtocol < url.indexOf('/')))
233             throw new java.net.MalformedURLException("invalid protocol: " +
234                 url.substring(0, checkProtocol));
235
236         if (url.startsWith("//")) {
237             final int startHost = 2;
238             int nextSlash = url.indexOf("/", startHost);
239             if (nextSlash >= 0) {
240                 startFile = nextSlash + 1;
241             } else {
242                 // no trailing slash implies no name
243                 nextSlash = url.length();
244                 startFile = nextSlash;
245             }
246
247             int colon = url.indexOf(":", startHost);
248             if ((colon > 1) && (colon < nextSlash)) {
249                 // explicit port supplied
250                 try {
251                     parsed.port =
252                         Integer.parseInt(url.substring(colon + 1,
253                                                        nextSlash));
254                 } catch (NumberFormatException e) {
255                     throw new MalformedURLException(
256                         "invalid port number: " + url);
257                 }
258             }
259
260             // if have colon then endhost, else end with slash
261             int endHost;
262             if (colon >= startHost) {
263                 endHost = colon;
264             } else {
265                 endHost = nextSlash;
266             }
267             parsed.host = url.substring(startHost, endHost);
268
269         } else if (url.startsWith("/")) {
270             startFile = 1;
271         } else {
272             startFile = 0;
273         }
274         // set the bind name
275         parsed.name = url.substring(startFile);
276         if (parsed.name.equals("") || parsed.name.equals("/")) {
277             parsed.name = null;
278         }
279
280         return parsed;
281     }
..............


However, in 1.4.X/1.5, URI#getHost() returns null and URI#getPort() returns -1
because  "appsrv_k01" doesn't follow RF2396. The hostname includes '_'.
In the above case, Naming#parseURL() seems to consider as hostname and port
are abbreviated . The parseURL sets the hostname and port number to localhost IPaddress and 1089 respectively and keep processing.


===  ./j2se/src/share/classes/java/rmi/Naming.java (1.5b62) ====
.............

   216      private static ParsedNamingURL parseURL(String str)
   217          throws MalformedURLException
   218      {
   219          try {
   220              URI uri = new URI(str);
   221              if (uri.getFragment() != null) {
   222                  throw new MalformedURLException(
   223                      "invalid character, '#', in URL name: " + str);
   224              } else if (uri.getQuery() != null) {
   225                  throw new MalformedURLException(
   226                      "invalid character, '?', in URL name: " + str);
   227              } else if (uri.getUserInfo() != null) {
   228                  throw new MalformedURLException(
   229                      "invalid character, '@', in URL host: " + str);
   230              }
   231              String scheme = uri.getScheme();
   232              if (scheme != null && !scheme.equals("rmi")) {
   233                  throw new MalformedURLException("invalid URL scheme: " + str);
   234              }
   235
   236              String name = uri.getPath();
   237              if (name != null) {
   238                  if (name.startsWith("/")) {
   239                      name = name.substring(1);
   240                  }
   241                  if (name.length() == 0) {
   242                      name = null;
   243                  }
   244              }
   245
   246              String host = uri.getHost();
   247              if (host == null) {
   248                  host = "";
   249                  if (uri.getPort() == -1) {
   250                      /* handle URIs with explicit port but no host
   251                       * (e.g., "//:1098/foo"); although they do not strictly
   252                       * conform to RFC 2396, Naming's javadoc explicitly allows
   253                       * them.
   254                       */
   255                      String authority = uri.getAuthority();
   256                      if (authority != null && authority.startsWith(":")) {
   257                          authority = "localhost" + authority;
   258                          uri = new URI(null, authority, null, null, null);
   259                      }
   260                  }
   261              }
   262              int port = uri.getPort();
   263              if (port == -1) {
   264                  port = Registry.REGISTRY_PORT;
   265              }
   266              return new ParsedNamingURL(host, port, name);
   267
   268          } catch (URISyntaxException ex) {

...........

PROBLEM :
  When a program uses the result of Naming#parseURL() as arguments 
  for Naming#rebind,

 If the specifications is "argument for rebind should follow RFC 2396",
 MalformedURLException should be thrown by some method(parseURL or 
 getHost ...?)
 At least, to consider "host name is abbreviated" doesn't seem reasonable.

 If the specifications is "argument for rebind may not follow RFC 2396",
 the hostname which includes '_' should be extracted. the current behavior 
 in 1.4.X/1.5 to set hostname to local IP address doesn't seem reasonable.

REQUEST :
  To clarify which behaviors is reasonable in rmi, in 1.3.1 and 1.4.X/1.5


TEST PROGRAM:
  Please try the following program on the host which name includes '_'.

----- a.java -->
import java.net.*;
import java.rmi.*;
import java.rmi.registry.*;
import java.rmi.server.*;
class a
{
  public static void main(String[] arg)
  {
    try {
      int p = Integer.parseInt(arg[0]);
      String host = InetAddress.getLocalHost().getHostName();
      x xr = new x();
      LocateRegistry.createRegistry(p);
      try {
        Naming.rebind("rmi://" + host + ":"+p+"/xxx", xr);
        System.out.println("ok");
      } catch (Exception e) {
        System.out.println("ng");
        e.printStackTrace();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.exit(0);
  }
  interface z extends Remote {
    public void z() throws RemoteException;
  }
  static class x extends UnicastRemoteObject implements z
  {
    x() throws RemoteException {}
    public void z() {}
  }
}
<----

HOW TO CONFIRM:
  - Launch the commnads in 1.5 as follows and you will see the similar
    message.

K:\shares2\rmi-rfc2396-diff-131-141>javac a.java

K:\shares2\rmi-rfc2396-diff-131-141>rmic a$x

K:\shares2\rmi-rfc2396-diff-131-141>java a 12200

java.rmi.ConnectException: Connection refused to host: xxx.yyy.zzz.uuu; nested exception is:
        java.net.ConnectException: Connection refused: connect
        at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:567)
        at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:185)
        at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:171)
        at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:313)
        at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
        at java.rmi.Naming.rebind(Naming.java:160)
        at a.main(a.java:15)
Caused by: java.net.ConnectException: Connection refused: connect
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:305)
        at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:171)
        at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:158)
        at java.net.Socket.connect(Socket.java:452)
        at java.net.Socket.connect(Socket.java:402)
        at java.net.Socket.<init>(Socket.java:309)
        at java.net.Socket.<init>(Socket.java:124)
        at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:22)
        at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:128)
        at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:562)
        ... 6 more

K:\shares2\rmi-rfc2396-diff-131-141>


If you try in 1.3.1_0X, you will see only "o.k".


=========================================================================================

Comments
WORK AROUND Do not use the java.rmi.Naming "convenience" API at all (it is just a URL-oriented wrapper around other, non-URL-oriented public APIs); instead, directly use one of the LocateRegistry.getRegistry methods to fabricate a registry stub and the Registry interface methods on that stub to communicate with the registry (see Evaluation). ###@###.### 2004-08-06
06-08-2004

EVALUATION This bug report is closely related to 4641504, which is also about the fact that certain previously-accepted "URL-formatted" names are no longer accepted by java.rmi.Naming since its implementation was updated for 1.4 to better conform to the URL syntax rules of RFC 2396 by using java.net.URI (which was done to fix other problems...). The general intention of the java.rmi.Naming API is that it should pretty much conform to standard URL syntax rules, which are now specified in RFC 2396 and RFC 2732 (this intention should, however, be more precisely described in the java.rmi.Naming specification). Supporting a URL hostname component containing an underscore seems dubious and not commonly useful; if such a host name must be used, I would strongly recommend not using the java.rmi.Naming "convenience" API at all (see below). As the customer has observed, if a URL-formatted name passed to java.rmi.Naming does not parse to contain a server-based naming authority, then it is always assumed to refer to the registry on the local host (and on the default port, if the authority component does not start with ':'). This behavior is appropriate for certain degenerate cases that must be supported (the java.rmi.Naming specification does say that the host component is optional), but it does seem that the implementation should be more intelligent about distinguishing such cases from an arbitrary authority component that cannot be parsed as server-based; in the general case, it would seem appropriate to throw a MalformedURLException, because the behavior of java.rmi.Naming requires understanding a host and port specified (explicitly or implicitly) by the supplied name-- a registry-based authority is meaningless in this context (you could say that java.rmi.Naming is like one of the "common situations" described in the documentation for the URI.parseServerAuthority method). This bug report will be used to represent the problem described in this paragraph. Note that java.rmi.Naming is merely a "convenience" API (of questionable value...)-- all of the functionality of java.rmi.Naming can be achieved with the separate steps of using one of the java.rmi.registry.LocateRegistry.getRegistry methods to create a stub to the registry at a given host/port (without any URL parsing issues) and then invoking methods of the java.rmi.registry.Registry interface on the resulting registry stub. (Using LocateRegistry.getRegistry is generally more flexible than using java.rmi.Naming; for example, it is necessary if a custom client socket factory needs to be used for registry communication.) ###@###.### 2004-08-06
06-08-2004