[JAVA-504] fetchMaxBsonObjectSize incorrectly assumes access to the admin database Created: 11/Jan/12  Updated: 11/Mar/13  Resolved: 11/Mar/13

Status: Closed
Project: Java Driver
Component/s: Connection Management
Affects Version/s: 2.5.3
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Peter Robinett Assignee: Jeffrey Yemin
Resolution: Done Votes: 1
Labels: authentication
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Using the library via the Casbah Scala library



 Description   

Some Mongo database servers do not give average accounts access to the admin database. This causes problems when attempting to make an authorized connection to a normal database.

When attempting to connect to such a machine, I get errors like this:

{{Jan 11, 2012 3:56:24 PM com.mongodb.DBTCPConnector fetchMaxBsonObjectSize
WARNING: null
java.io.IOException: couldn't connect to [/0.0.39.87:27017] bc:java.net.SocketException: Invalid argument or cannot assign requested address
at com.mongodb.DBPort._open(DBPort.java:206)
at com.mongodb.DBPort.go(DBPort.java:94)
at com.mongodb.DBPort.go(DBPort.java:75)
at com.mongodb.DBPort.findOne(DBPort.java:129)
at com.mongodb.DBPort.runCommand(DBPort.java:138)
at com.mongodb.DBTCPConnector.fetchMaxBsonObjectSize(DBTCPConnector.java:409)
at com.mongodb.DBTCPConnector.checkMaster(DBTCPConnector.java:396)
at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:192)
at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:295)
at com.mongodb.DB.command(DB.java:152)
at com.mongodb.DB._doauth(DB.java:496)
at com.mongodb.DB.authenticate(DB.java:433)
at com.mongodb.casbah.MongoDB.authenticate(MongoDB.scala:74)}}

I can't say what the exact solution is, but it appears that fetchMaxBsonObjectSize() returns a null in this situation, with its unexpected presence (an int is expected) then causing the exception in later commands like DB.authenticate().



 Comments   
Comment by Jeffrey Yemin [ 06/Mar/12 ]

In order for the connection to work reliably, please ensure that all the IP addresses that DNS returns for the host are accessible from the machine on which you're running the client program. If you were getting a connection exception to 0.0.39.97 reported by the Java driver, it means that at the time you ran the program DNS was reporting that as one of the IP addresses for staff.mongohq.com.

Comment by Peter Robinett [ 06/Mar/12 ]

No, I didn't but since I was able to reproduce the original issue on my local machine once attempting to MongoHQ instead of my local test Mongo instance, I am confident it's the same.

Comment by Jeffrey Yemin [ 06/Mar/12 ]

Did you run this test on the same machine that was giving you the connection exception to 0.0.39.97? If so, can you try the test with the Java driver again and see if it's still broken?

Comment by Jeffrey Yemin [ 06/Mar/12 ]

Try taking the username/password out of the URI. It is not parsed correctly in 2.5.3.

Comment by Peter Robinett [ 06/Mar/12 ]

Stephen's workaround kind of works for me. What doesn't work is the URL parsing:

scala> val uri = new com.mongodb.MongoURI(url)
uri: com.mongodb.MongoURI = mongodb://<username>:<password>@staff.mongohq.com:10071/<database>
 
scala> uri.getHosts
res0: java.util.List[java.lang.String] = [10071]
 
scala> uri.getUsername
res1: java.lang.String = <username>:<password>
 
scala> uri.getPassword
res2: Array[Char] = Array(s, t, a, f, f, ., m, o, n, g, o, h, q, ., c, o, m)

I can manually connect and authenticate, though:

scala> val m = new Mongo("staff.mongohq.com", 10071)
m: com.mongodb.Mongo = Mongo: staff.mongohq.com:10071
 
scala> val db = m.getDB(uri.getDatabase)
db: com.mongodb.DB = <database>
 
scala> db.authenticate("<username>", "<password>".toCharArray)
res3: Boolean = true

Comment by Peter Robinett [ 06/Mar/12 ]

Jeff, that works fine from my development machine:

First

scala> val host = "staff.mongohq.com"
host: java.lang.String = staff.mongohq.com
 
scala> val port = 10071
port: Int = 10071
 
scala> import java.net._
import java.net._
 
scala> :paste
// Entering paste mode (ctrl-D to finish)
 
val all =  InetAddress.getAllByName(host);
for (cur <- all) {
  println("Inet Address: " + cur.getClass() + " -- " + cur)
}
 
// Exiting paste mode, now interpreting.
 
Inet Address: class java.net.Inet4Address -- staff.mongohq.com/50.17.135.240
all: Array[java.net.InetAddress] = Array(staff.mongohq.com/50.17.135.240)

and then

scala> import javax.net._
import javax.net._
 
scala> import java.io._
import java.io._
 
scala> :paste
// Entering paste mode (ctrl-D to finish)
 
for (cur <- all) {
    val s = SocketFactory.getDefault().createSocket();
    val socketAddress = new InetSocketAddress(cur, port);
    try {
        println("------");
        println("Connecting to: " + socketAddress);
        s.connect(socketAddress, 1000);
        println("Connected to: " + socketAddress);
    } catch {
      case e: IOException => {
        println("Could not connect to: " + socketAddress)
        println(e.getMessage())
        println(e.getClass())
      }
    } finally {
        s.close()
    }
}
 
// Exiting paste mode, now interpreting.
 
------
Connecting to: staff.mongohq.com/50.17.135.240:10071
Connected to: staff.mongohq.com/50.17.135.240:10071

Comment by Jeffrey Yemin [ 06/Mar/12 ]

Please try running this standalone program(replace host and port with whatever you are using for mongo) and paste the output.

 
        String host = "localhost";
        int port = 27017;
 
        InetAddress[] all =  InetAddress.getAllByName(host);
        for (InetAddress cur: all) {
            System.out.println("Inet Address: " + cur.getClass() + " -- " + cur);
        }
 
 
        for (InetAddress cur: all) {
            Socket s = SocketFactory.getDefault().createSocket();
            InetSocketAddress socketAddress = new InetSocketAddress(cur, port);
            try {
                System.out.println();
                System.out.println("Connecting to: " + socketAddress);
                s.connect(socketAddress, 1000);
                System.out.println("Connected to: " + socketAddress);
            } catch (IOException e) {
                System.out.println("Could not connect to: " + socketAddress);
                System.out.println(e.getMessage());
                System.out.println(e.getClass());
            } finally {
                s.close();
            }
        }

Comment by Stephan B [ 06/Mar/12 ]

Haha, there is a workaround (in Scala):

val uri = new MongoURI(System.getenv("MONGOHQ_URL"))

val url = uri.getHosts.get(0)
val host = url.substring(0, url.indexOf(":"))
val port = url.substring(host.length() + 1).toInt
log.info("connecting to remote MongoDB: {}:{}", host, port)
val m = new Mongo(host, port)

log.info("accessing remote MongoDB: {}", uri.getDatabase)
val db = m.getDB(uri.getDatabase)

log.info("authenticating at remote MongoDB: {}@{}", uri.getUsername, uri.getPassword)
db.authenticate(uri.getUsername, uri.getPassword)

Comment by Stephan B [ 05/Mar/12 ]

Is this something that will be fixed? I have the exact same problem now when trying to use MongoHQ

Comment by Peter Robinett [ 18/Jan/12 ]

To the real address, yes:

$ telnet staff.mongohq.com 10071
Trying 50.17.135.240...
Connected to ec2-50-17-135-240.compute-1.amazonaws.com.
Escape character is '^]'.

To the mysterious 0.0.39.97 address that somehow appears, no:

$ telnet 0.0.39.97 27017
Trying 0.0.39.97...
telnet: connect to address 0.0.39.97: No route to host
telnet: Unable to connect to remote host

Comment by Jeffrey Yemin [ 18/Jan/12 ]

Are you able to telnet to that host and port? You should see something like:

$ telnet 0.0.39.97 27017
Trying 0.0.39.97...
Connected to localhost.
Escape character is '^]'.

Comment by Peter Robinett [ 18/Jan/12 ]

So the null is coming from somewhere else. Hmm...

I get a similar but slightly different error if I skip the authentication step. From the Scala REPL:

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29).
Type in expressions to have them evaluated.
Type :help for more information.
 
scala> import com.mongodb.casbah.Imports._
import com.mongodb.casbah.Imports._
 
scala> import com.mongodb.casbah.commons.conversions.scala._
import com.mongodb.casbah.commons.conversions.scala._
 
scala> import com.mongodb.casbah.MongoURI
import com.mongodb.casbah.MongoURI
 
scala> val server = "mongodb://<username>:<password>@staff.mongohq.com:10071/<database>"
server: java.lang.String = mongodb://<username>:<password>@staff.mongohq.com:10071/<database>
 
scala> val uri = MongoURI(server)
uri: com.mongodb.casbah.MongoURI = mongodb://<username>:<password>@staff.mongohq.com:10071/<database>
 
scala> val mongo = MongoConnection(uri)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
mongo: com.mongodb.casbah.MongoConnection = com.mongodb.casbah.MongoConnection@72a2890a
 
scala> val db = mongo(uri.database)
db: com.mongodb.casbah.MongoDB = <database>
 
scala> val ratesCollection = db("rates")
Jan 18, 2012 12:05:51 PM com.mongodb.DBTCPConnector fetchMaxBsonObjectSize
WARNING: null
java.io.IOException: couldn't connect to [/0.0.39.87:27017] bc:java.net.NoRouteToHostException: No route to host
	at com.mongodb.DBPort._open(DBPort.java:206)
	at com.mongodb.DBPort.go(DBPort.java:94)
	at com.mongodb.DBPort.go(DBPort.java:75)
	at com.mongodb.DBPort.findOne(DBPort.java:129)
	at com.mongodb.DBPort.runCommand(DBPort.java:138)
	at com.mongodb.DBTCPConnector.fetchMaxBsonObjectSize(DBTCPConnector.java:409)
	at com.mongodb.DBTCPConnector.checkMaster(DBTCPConnector.java:396)
	at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:192)
	at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:295)
	at com.mongodb.DBCursor._check(DBCursor.java:354)
	at com.mongodb.DBCursor._hasNext(DBCursor.java:484)
	at com.mongodb.DBCursor.hasNext(DBCursor.java:509)
	at com.mongodb.casbah.MongoCursorBase$class.hasNext(MongoCursor.scala:73)
	at com.mongodb.casbah.MongoCursor.hasNext(MongoCursor.scala:497)
	at scala.collection.Iterator$class.foreach(Iterator.scala:660)
	at com.mongodb.casbah.MongoCursor.foreach(MongoCursor.scala:497)
	at scala.collection.IterableLike$class.foreach(IterableLike.scala:73)
	at com.mongodb.casbah.MongoCollection.foreach(MongoCollection.scala:920)
	at scala.collection.TraversableOnce$class.addString(TraversableOnce.scala:285)
	at com.mongodb.casbah.MongoCollection.addString(MongoCollection.scala:920)
	at scala.collection.TraversableOnce$class.mkString(TraversableOnce.scala:263)
	at com.mongodb.casbah.MongoCollection.mkString(MongoCollection.scala:920)
	at scala.collection.TraversableLike$class.toString(TraversableLike.scala:579)
	at com.mongodb.casbah.MongoCollection.toString(MongoCollection.scala:920)

Comment by Jeffrey Yemin [ 17/Jan/12 ]

I don't think this has anything to do with authentication. It's just that DB.authenticate is the first time the driver tries to connect to the server. For example, a simple test program trying to authenticate to a database that is down will generate this stack trace:

java.io.IOException: couldn't connect to [/127.0.0.1:40017] bc:java.net.ConnectException: Connection refused
	at com.mongodb.DBPort._open(DBPort.java:228)
	at com.mongodb.DBPort.go(DBPort.java:112)
	at com.mongodb.DBPort.go(DBPort.java:93)
	at com.mongodb.DBPort.findOne(DBPort.java:146)
	at com.mongodb.DBPort.runCommand(DBPort.java:157)
	at com.mongodb.DBTCPConnector.fetchMaxBsonObjectSize(DBTCPConnector.java:457)
	at com.mongodb.DBTCPConnector.checkMaster(DBTCPConnector.java:444)
	at com.mongodb.DBTCPConnector.call(DBTCPConnector.java:209)
	at com.mongodb.DBApiLayer$MyCollection.__find(DBApiLayer.java:305)
	at com.mongodb.DB.command(DB.java:160)
	at com.mongodb.DB.command(DB.java:183)
	at com.mongodb.DB.command(DB.java:144)
	at com.mongodb.DB._doauth(DB.java:541)
	at com.mongodb.DB.authenticate(DB.java:478)
	at com.mongodb.perf.AuthTest.main(AuthTest.java:19)

For a non-authenticated database, you just get the exception in a later call (like find(...)). Just to be sure, can you confirm that you get the same exception calling find() on a collection in a non-authenticated database?

With regard to the actual message in the SocketException, I've never seen that before. I found a couple of threads on it (like http://forums.mysql.com/read.php?39,41308,41308#msg-41308), but I can't vouch for the accuracy.

By the way, DBTCPConnector.fetchMaxBsonObjectSize returns 0 if it can't reach the server (since the return value is int, it can't return null). In that case the driver will default to 4MB (com.mongodb.Bytes#MAX_OBJECT_SIZE).

Generated at Thu Feb 08 08:52:26 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.