Bug 56661 - ServletRequest#getLocalAddr() returns the hostname, not the IP address
Summary: ServletRequest#getLocalAddr() returns the hostname, not the IP address
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 6
Classification: Unclassified
Component: Connectors (show other bugs)
Version: 6.0.41
Hardware: All Mac OS X 10.4
: P2 normal (vote)
Target Milestone: ----
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-06-24 00:10 UTC by Palle Girgensohn
Modified: 2014-07-30 09:08 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Palle Girgensohn 2014-06-24 00:10:58 UTC
request.getLocalAddr() does not return an IP-address, but the host name from the http header. 

Accessing http://127.0.0.1:8080/ will make getLocalAddr() return 127.0.0.1
Accessing http://localhost:8080/ will make getLocalAddr() return localhost
Accessing http://localhost/ using mod_jk/ajp13 will also make getLocalAddr() return localhost

I see this on java7 using MacOS and FreeBSD.
Comment 1 Christopher Schultz 2014-06-24 16:28:56 UTC
Not yet verified (by me), but the Servlet API Javadoc pretty clearly says this method should return an IP address (and not a hostname).
Comment 2 Mark Thomas 2014-07-04 19:38:25 UTC
Tested with 7.0.x and 8.0.x on OSX and request.getLocalAddr() returns an IP address when accessed via http://localhost:8080/...
Comment 3 Palle Girgensohn 2014-07-05 00:02:45 UTC
I do get 0:0:0:0:0:0:0:1 when accessing http://localhost:8080/ now, that might just have been a mistake.

It seems that the problem is with the AJP Connector?

$ cat foo.jsp 
	<%= request.getLocalAddr() %>
$ curl  http://localhost/foo.jsp
	localhost
$ curl -k https://localhost/foo.jsp
	localhost

This holds true for tomcat 7.0.54 on FreeBSD and Mac OS X.
Comment 4 Konstantin Preißer 2014-07-05 00:20:10 UTC
Hi,

I can confirm that this problem happens with Tomcat 8.0.9 when using AJP NIO connector (I used the ISAPI Redirector with IIS).
In this case, request.getLocalAddr() returns the name of the "Host" header instead of an IP address, e.g. "localhost".

If using the HTTP NIO connector, the problem does not occur; request.getLocalAddr() will return an IP address in this case, e.g. "0:0:0:0:0:0:0:1".
Comment 5 Mark Thomas 2014-07-06 22:31:26 UTC
All AJP connectors behave this way and have done since getLocalAddr() was added in Servlet 2.4.

The problem is that the AJP protocol passes the remoteAddr, remoteHost, localName and localPort but *NOT* the localAddr.

localAddr should be the IP address the client used to contact the server and this simply is not available - hence why the host name was used.

Possible solutions include modifying the AJP protocol but this is non-trivial.

A simple work-around would be to have the reverse proxy add a custom header with the localAddr information and then have a Valve / Filter read that header value and then present it to the application.

I'm leaning towards resolving this as WONTFIX.
Comment 6 Christopher Schultz 2014-07-07 15:18:18 UTC
Technically, this shouldn't require a change to the AJP protocol, as arbitrary HTTP headers can be sent just by modifying the configuration.

Unfortunately, the CGI spec does not specify anything like "localAddr"... the closest thing available is SERVER_NAME which ... comes from the client's "Host" header. I suspect this is what is being used for getLocalAddr, here.

Reading a bit about httpd's capabilities, I'm not sure it's actually possible to get the IP address of the server interface that accepted the request from the client. If the address *can* be obtained, sending it via an HTTP header and using something like the RemoteIpValve would probably work.
Comment 7 Mark Thomas 2014-07-07 16:17:37 UTC
I've just taken a look at the httpd docs as well and I don't see any way of obtaining the IP address of the interface that the request was received on. That reduces the choices to:
a) returning null
b) return the hose name (current behaviour)
c) use DNS to get the IP address
d) return an empty string

If I was starting from a clean slate, I choose option a). However, given the current behaviour that is likely to start triggering NPEs in applications. That makes d) preferrable to a).

There are many cases where c) will not return the correct value. DNS round-robin load-balancing being one. Therefore, I don't think this apporach should be used.

So that leaves b) or d) as the possible options in this case. I don't think we should change it for 7.0.x or earlier but we could change it in 8.0x onwards.

I leave it a few days before doing anything to give folks a chance to comment.
Comment 8 Mark Thomas 2014-07-07 16:18:52 UTC
Just re=read my previous comment and it isn't very clear.

My proposal is to leave 7.0.x and earlier unchanged and to change 8.0.x onwards to return the empty string by default for getLocalAddr()
Comment 9 Konstantin Kolinko 2014-07-07 16:26:44 UTC
(In reply to Mark Thomas from comment #7)
> I've just taken a look at the httpd docs as well and I don't see any way of
> obtaining the IP address of the interface that the request was received on.

Looking at CustomLog formats [1] it says that "%A" is "Local IP-address". Thus there should be a way.

There is also such feature as IP-based virtual hosts. [2]


[1] http://httpd.apache.org/docs/2.4/mod/mod_log_config.html#formats
[2] http://httpd.apache.org/docs/2.4/vhosts/ip-based.html
Comment 10 Konstantin Kolinko 2014-07-07 16:42:30 UTC
There is such directive as
 JkOptions  +ForwardLocalAddress

http://tomcat.apache.org/connectors-doc/webserver_howto/apache.html

Commit that implemented it:
http://marc.info/?l=tomcat-dev&m=111545377731908&w=2

>> r->connection->local_ip
Comment 11 Konstantin Kolinko 2014-07-07 16:53:59 UTC
As examples:
Implementation of custom "JK_LB_ACTIVATION" request attribute: r1078846
Implementation of custom "AJP_REMOTE_PORT" request attribute: r756926 r757223

Those are the only custom header names in native/common/jk_ajp_common.h of mod_jk, so I think those are the only examples.
Comment 12 Mark Thomas 2014-07-07 19:57:04 UTC
OK. Looks like the information is available in the httpd internals but not (obviously) via config. Therefore, if mod_jk implements a new request atribute to pass this information, Tomcat can use it for getLocalAddr(). Moving this to mod_jk to get that implemented.
Comment 13 Rainer Jung 2014-07-10 23:33:11 UTC
Implemented new request attribute "AJP_LOCAL_ADDR" in mod_jk r1609589.

Will be part of version 1.2.41.

Assigning issue back to Tomcat for implementing the use of the new attribute.
Comment 14 Rainer Jung 2014-07-11 00:01:14 UTC
I added support for the new attribute to TC 8 in r1609593.
Needs some more testing before porting back to TC7 an probably 6.

A full test needs a dev build of mod_jk r1609589 or later.

Alternatively one can try in Apache:

SetEnvIf Server_Addr "(.*)" AJP_LOCAL_ADDR=$1
JkEnvVar AJP_LOCAL_ADDR

Which for current releases of mod_jk would send a request attribute named AJP_LOCAL_ADDR with its value set to the value of the Apache internal variable SERVER_ADDR which in turn contains the local IP address of Apache.

Starting with mod_jk 1.2.41 the attribute will be set without explicit configuration.
Comment 15 Rainer Jung 2014-07-11 02:01:02 UTC
Test looked good.
Ported by to TC 7 in r1609606 and proposed for TC 6.
Comment 16 Rainer Jung 2014-07-14 06:09:15 UTC
Forwarding the local IP address as the custom request attribute will also be part of mod_proxy_ajp in the Apache web server 2.4.10. Adding it to the next release of Apache httpd 2.2 has been proposed as well.
Comment 17 Mark Thomas 2014-07-29 09:42:35 UTC
Fixed in 6.0.x for 6.0.42 onwards.
Comment 18 Eugene Chung (TmaxSoft) 2014-07-30 08:14:30 UTC
I'm a bit confused with the definition of getLocalXXX() APIs.
Should they return the web server(intermediary in view of WAS)'s information?
Comment 19 Rainer Jung 2014-07-30 09:08:45 UTC
(In reply to Eugene Chung (TmaxSoft) from comment #18)
> I'm a bit confused with the definition of getLocalXXX() APIs.
> Should they return the web server(intermediary in view of WAS)'s information?

I haven't checked the Spec but I don't it will clarify the behavior for that situation.

One of the major ideas behind the AJP protocol is to transport the communication data, that the intermediary saw, to the backend and let the application see that information as if the backend was placed at the position of the intermediary. One goal is to make sure, that any self-referential URL the application creates will actually point to the intermediary and not the the backend.

That's why we have chosen to return the local IP of the intermediary from the AJP connector.