diff --git a/netwerk/base/nsIServerSocket.idl b/netwerk/base/nsIServerSocket.idl index fa54104c333c..909778279753 100644 --- a/netwerk/base/nsIServerSocket.idl +++ b/netwerk/base/nsIServerSocket.idl @@ -150,6 +150,24 @@ interface nsIServerSocket : nsISupports void initWithFilename(in nsIFile aPath, in unsigned long aPermissions, in long aBacklog); + /** + * initWithAbstractAddress + * + * This mehtod is a flavor of initWithFilename method. This initializes + * a UNIX domain socket that uses abstract socket address. + * This socket type is only supported on Linux and Android. + * + * On systems that don't support this type's UNIX domain sockets at all, + * this returns NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED. + * + * @param aName + * The abstract socket address which the socket should be created. + * @param aBacklog + * The maximum length the queue of pending connections may grow to. + */ + void initWithAbstractAddress(in AUTF8String aName, + in long aBacklog); + /** * close * diff --git a/netwerk/base/nsISocketTransportService.idl b/netwerk/base/nsISocketTransportService.idl index 8e3bdd44d74d..9427e9708b61 100644 --- a/netwerk/base/nsISocketTransportService.idl +++ b/netwerk/base/nsISocketTransportService.idl @@ -84,6 +84,20 @@ interface nsISocketTransportService : nsISupports */ nsISocketTransport createUnixDomainTransport(in nsIFile aPath); + /** + * Create a transport built on a Unix domain socket that uses abstract + * address name. + * + * If abstract socket address isn't supported on System, this returns + * NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED. + * + * @param aName + * The name of abstract socket adress of the Unix domain socket to + * which we should connect. + */ + nsISocketTransport + createUnixDomainAbstractAddressTransport(in ACString aName); + /** * Adds a new socket to the list of controlled sockets. * diff --git a/netwerk/base/nsServerSocket.cpp b/netwerk/base/nsServerSocket.cpp index 521804d16f9e..13630882a6c3 100644 --- a/netwerk/base/nsServerSocket.cpp +++ b/netwerk/base/nsServerSocket.cpp @@ -319,6 +319,29 @@ nsServerSocket::InitWithFilename(nsIFile *aPath, uint32_t aPermissions, int32_t #endif } +NS_IMETHODIMP +nsServerSocket::InitWithAbstractAddress(const nsACString& aName, + int32_t aBacklog) +{ + // Abstract socket address is supported on Linux and Android only. + // If not Linux, we should return error. +#if defined(XP_LINUX) + // Create an abstract socket address PRNetAddr referring to the name + PRNetAddr addr; + if (aName.Length() > sizeof(addr.local.path) - 2) { + return NS_ERROR_FILE_NAME_TOO_LONG; + } + addr.local.family = PR_AF_LOCAL; + addr.local.path[0] = 0; + memcpy(addr.local.path + 1, aName.BeginReading(), aName.Length()); + addr.local.path[aName.Length() + 1] = 0; + + return InitWithAddress(&addr, aBacklog); +#else + return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; +#endif +} + NS_IMETHODIMP nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags, int32_t aBackLog) diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp index 1b50e59ed6d3..e606e571007f 100644 --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -928,18 +928,34 @@ nsSocketTransport::Init(const char **types, uint32_t typeCount, nsresult nsSocketTransport::InitWithFilename(const char *filename) { - size_t filenameLength = strlen(filename); + return InitWithName(filename, strlen(filename)); +} - if (filenameLength > sizeof(mNetAddr.local.path) - 1) +nsresult +nsSocketTransport::InitWithName(const char *name, size_t length) +{ + if (length > sizeof(mNetAddr.local.path) - 1) { return NS_ERROR_FILE_NAME_TOO_LONG; + } - mHost.Assign(filename); + if (!name[0] && length > 1) { + // name is abstract address name that is supported on Linux only +#if defined(XP_LINUX) + mHost.Assign(name + 1, length - 1); +#else + return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; +#endif + } else { + // The name isn't abstract socket address. So this is Unix domain + // socket that has file path. + mHost.Assign(name, length); + } mPort = 0; mTypeCount = 0; mNetAddr.local.family = AF_LOCAL; - memcpy(mNetAddr.local.path, filename, filenameLength); - mNetAddr.local.path[filenameLength] = '\0'; + memcpy(mNetAddr.local.path, name, length); + mNetAddr.local.path[length] = '\0'; mNetAddrIsSet = true; return NS_OK; diff --git a/netwerk/base/nsSocketTransport2.h b/netwerk/base/nsSocketTransport2.h index 41ae9eca8c9c..6d151f57da9d 100644 --- a/netwerk/base/nsSocketTransport2.h +++ b/netwerk/base/nsSocketTransport2.h @@ -150,6 +150,13 @@ public: // connected to the given Unix domain address. We can only create // unlayered, simple, stream sockets. nsresult InitWithFilename(const char *filename); + + // This method instructs the socket transport to open a socket + // connected to the given Unix domain address that includes abstract + // socket address. If using abstract socket address, first character of + // name parameter has to be \0. + // We can only create unlayered, simple, stream sockets. + nsresult InitWithName(const char *name, size_t len); #endif // nsASocketHandler methods: diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp index df2cc43a35f5..9359d38ccdc7 100644 --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -845,6 +845,30 @@ nsSocketTransportService::CreateUnixDomainTransport(nsIFile *aPath, #endif } +NS_IMETHODIMP +nsSocketTransportService::CreateUnixDomainAbstractAddressTransport( + const nsACString& aName, + nsISocketTransport **result) +{ + // Abstract socket address is supported on Linux only +#ifdef XP_LINUX + RefPtr trans = new nsSocketTransport(); + // First character of Abstract socket address is null + UniquePtr name(new char[aName.Length() + 1]); + *(name.get()) = 0; + memcpy(name.get() + 1, aName.BeginReading(), aName.Length()); + nsresult rv = trans->InitWithName(name.get(), aName.Length() + 1); + if (NS_FAILED(rv)) { + return rv; + } + + trans.forget(result); + return NS_OK; +#else + return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED; +#endif +} + NS_IMETHODIMP nsSocketTransportService::OnDispatchedEvent() { diff --git a/netwerk/test/unit/test_unix_domain.js b/netwerk/test/unit/test_unix_domain.js index 1d301050b143..fbeb6932b0c2 100644 --- a/netwerk/test/unit/test_unix_domain.js +++ b/netwerk/test/unit/test_unix_domain.js @@ -5,6 +5,9 @@ var CC = Components.Constructor; const UnixServerSocket = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "initWithFilename"); +const UnixAbstractServerSocket = CC("@mozilla.org/network/server-socket;1", + "nsIServerSocket", + "initWithAbstractAddress"); const ScriptableInputStream = CC("@mozilla.org/scriptableinputstream;1", "nsIScriptableInputStream", @@ -27,16 +30,24 @@ function run_test() return; } - add_test(test_echo); - add_test(test_name_too_long); - add_test(test_no_directory); - add_test(test_no_such_socket); - add_test(test_address_in_use); - add_test(test_file_in_way); - add_test(test_create_permission); - add_test(test_connect_permission); - add_test(test_long_socket_name); - add_test(test_keep_when_offline); + // The xpcshell temp directory on Android doesn't seem to let us create + // Unix domain sockets. (Perhaps it's a FAT filesystem?) + if (mozinfo.os != "android") { + add_test(test_echo); + add_test(test_name_too_long); + add_test(test_no_directory); + add_test(test_no_such_socket); + add_test(test_address_in_use); + add_test(test_file_in_way); + add_test(test_create_permission); + add_test(test_connect_permission); + add_test(test_long_socket_name); + add_test(test_keep_when_offline); + } + + if (mozinfo.os == "android" || mozinfo.os == "linux") { + add_test(test_abstract_address_socket); + } run_next_test(); } @@ -543,3 +554,51 @@ function test_keep_when_offline() run_next_test(); } } + +function test_abstract_address_socket() +{ + const socketname = "abstractsocket"; + let server = new UnixAbstractServerSocket(socketname, -1); + server.asyncListen({ + onSocketAccepted: (aServ, aTransport) => { + let serverInput = aTransport.openInputStream(0, 0, 0) + .QueryInterface(Ci.nsIAsyncInputStream); + let serverOutput = aTransport.openOutputStream(0, 0, 0); + + serverInput.asyncWait(aStream => { + info("called test_abstract_address_socket's onSocketAccepted's onInputStreamReady"); + + // Receive data from the client, and send back a response. + let serverScriptableInput = new ScriptableInputStream(serverInput); + Assert.equal(serverScriptableInput.readBytes(9), "ping ping"); + serverOutput.write("pong", 4); + + }, 0, 0, threadManager.currentThread); + }, + onStopListening: (aServ, aTransport) => { + }, + }); + + let client = socketTransportService + .createUnixDomainAbstractAddressTransport(socketname); + Assert.equal(client.host, socketname); + Assert.equal(client.port, 0); + let clientInput = client.openInputStream(0, 0, 0) + .QueryInterface(Ci.nsIAsyncInputStream); + let clientOutput = client.openOutputStream(0, 0, 0); + + clientOutput.write("ping ping", 9); + + clientInput.asyncWait(aStream => { + let clientScriptInput = new ScriptableInputStream(clientInput); + let available = clientScriptInput.available(); + if (available) { + Assert.equal(clientScriptInput.readBytes(4), "pong"); + + client.close(Cr.NS_OK); + server.close(Cr.NS_OK); + + run_next_test(); + } + }, 0, 0, threadManager.currentThread); +} diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index c880e61ab946..e29835a32bff 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -336,9 +336,6 @@ run-sequentially = Hardcoded hash value includes port 4444. [test_about_protocol.js] [test_bug856978.js] [test_unix_domain.js] -# The xpcshell temp directory on Android doesn't seem to let us create -# Unix domain sockets. (Perhaps it's a FAT filesystem?) -skip-if = os == "android" [test_addr_in_use_error.js] [test_about_networking.js] [test_ping_aboutnetworking.js]