nsServerSocket.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /* vim:set ts=2 sw=2 et cindent: */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsSocketTransport2.h"
  6. #include "nsServerSocket.h"
  7. #include "nsProxyRelease.h"
  8. #include "nsAutoPtr.h"
  9. #include "nsError.h"
  10. #include "nsNetCID.h"
  11. #include "prnetdb.h"
  12. #include "prio.h"
  13. #include "nsThreadUtils.h"
  14. #include "mozilla/Attributes.h"
  15. #include "mozilla/EndianUtils.h"
  16. #include "mozilla/net/DNS.h"
  17. #include "nsServiceManagerUtils.h"
  18. #include "nsIFile.h"
  19. namespace mozilla { namespace net {
  20. static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
  21. //-----------------------------------------------------------------------------
  22. typedef void (nsServerSocket:: *nsServerSocketFunc)(void);
  23. static nsresult
  24. PostEvent(nsServerSocket *s, nsServerSocketFunc func)
  25. {
  26. nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(s, func);
  27. if (!gSocketTransportService)
  28. return NS_ERROR_FAILURE;
  29. return gSocketTransportService->Dispatch(ev, NS_DISPATCH_NORMAL);
  30. }
  31. //-----------------------------------------------------------------------------
  32. // nsServerSocket
  33. //-----------------------------------------------------------------------------
  34. nsServerSocket::nsServerSocket()
  35. : mFD(nullptr)
  36. , mLock("nsServerSocket.mLock")
  37. , mAttached(false)
  38. , mKeepWhenOffline(false)
  39. {
  40. // we want to be able to access the STS directly, and it may not have been
  41. // constructed yet. the STS constructor sets gSocketTransportService.
  42. if (!gSocketTransportService)
  43. {
  44. // This call can fail if we're offline, for example.
  45. nsCOMPtr<nsISocketTransportService> sts =
  46. do_GetService(kSocketTransportServiceCID);
  47. }
  48. // make sure the STS sticks around as long as we do
  49. NS_IF_ADDREF(gSocketTransportService);
  50. }
  51. nsServerSocket::~nsServerSocket()
  52. {
  53. Close(); // just in case :)
  54. // release our reference to the STS
  55. nsSocketTransportService *serv = gSocketTransportService;
  56. NS_IF_RELEASE(serv);
  57. }
  58. void
  59. nsServerSocket::OnMsgClose()
  60. {
  61. SOCKET_LOG(("nsServerSocket::OnMsgClose [this=%p]\n", this));
  62. if (NS_FAILED(mCondition))
  63. return;
  64. // tear down socket. this signals the STS to detach our socket handler.
  65. mCondition = NS_BINDING_ABORTED;
  66. // if we are attached, then we'll close the socket in our OnSocketDetached.
  67. // otherwise, call OnSocketDetached from here.
  68. if (!mAttached)
  69. OnSocketDetached(mFD);
  70. }
  71. void
  72. nsServerSocket::OnMsgAttach()
  73. {
  74. SOCKET_LOG(("nsServerSocket::OnMsgAttach [this=%p]\n", this));
  75. if (NS_FAILED(mCondition))
  76. return;
  77. mCondition = TryAttach();
  78. // if we hit an error while trying to attach then bail...
  79. if (NS_FAILED(mCondition))
  80. {
  81. NS_ASSERTION(!mAttached, "should not be attached already");
  82. OnSocketDetached(mFD);
  83. }
  84. }
  85. nsresult
  86. nsServerSocket::TryAttach()
  87. {
  88. nsresult rv;
  89. if (!gSocketTransportService)
  90. return NS_ERROR_FAILURE;
  91. //
  92. // find out if it is going to be ok to attach another socket to the STS.
  93. // if not then we have to wait for the STS to tell us that it is ok.
  94. // the notification is asynchronous, which means that when we could be
  95. // in a race to call AttachSocket once notified. for this reason, when
  96. // we get notified, we just re-enter this function. as a result, we are
  97. // sure to ask again before calling AttachSocket. in this way we deal
  98. // with the race condition. though it isn't the most elegant solution,
  99. // it is far simpler than trying to build a system that would guarantee
  100. // FIFO ordering (which wouldn't even be that valuable IMO). see bug
  101. // 194402 for more info.
  102. //
  103. if (!gSocketTransportService->CanAttachSocket())
  104. {
  105. nsCOMPtr<nsIRunnable> event =
  106. NewRunnableMethod(this, &nsServerSocket::OnMsgAttach);
  107. if (!event)
  108. return NS_ERROR_OUT_OF_MEMORY;
  109. nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
  110. if (NS_FAILED(rv))
  111. return rv;
  112. }
  113. //
  114. // ok, we can now attach our socket to the STS for polling
  115. //
  116. rv = gSocketTransportService->AttachSocket(mFD, this);
  117. if (NS_FAILED(rv))
  118. return rv;
  119. mAttached = true;
  120. //
  121. // now, configure our poll flags for listening...
  122. //
  123. mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
  124. return NS_OK;
  125. }
  126. void
  127. nsServerSocket::CreateClientTransport(PRFileDesc* aClientFD,
  128. const NetAddr& aClientAddr)
  129. {
  130. RefPtr<nsSocketTransport> trans = new nsSocketTransport;
  131. if (NS_WARN_IF(!trans)) {
  132. mCondition = NS_ERROR_OUT_OF_MEMORY;
  133. return;
  134. }
  135. nsresult rv = trans->InitWithConnectedSocket(aClientFD, &aClientAddr);
  136. if (NS_WARN_IF(NS_FAILED(rv))) {
  137. mCondition = rv;
  138. return;
  139. }
  140. mListener->OnSocketAccepted(this, trans);
  141. }
  142. //-----------------------------------------------------------------------------
  143. // nsServerSocket::nsASocketHandler
  144. //-----------------------------------------------------------------------------
  145. void
  146. nsServerSocket::OnSocketReady(PRFileDesc *fd, int16_t outFlags)
  147. {
  148. NS_ASSERTION(NS_SUCCEEDED(mCondition), "oops");
  149. NS_ASSERTION(mFD == fd, "wrong file descriptor");
  150. NS_ASSERTION(outFlags != -1, "unexpected timeout condition reached");
  151. if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL))
  152. {
  153. NS_WARNING("error polling on listening socket");
  154. mCondition = NS_ERROR_UNEXPECTED;
  155. return;
  156. }
  157. PRFileDesc *clientFD;
  158. PRNetAddr prClientAddr;
  159. NetAddr clientAddr;
  160. // NSPR doesn't tell us the peer address's length (as provided by the
  161. // 'accept' system call), so we can't distinguish between named,
  162. // unnamed, and abstract peer addresses. Clear prClientAddr first, so
  163. // that the path will at least be reliably empty for unnamed and
  164. // abstract addresses, and not garbage when the peer is unnamed.
  165. memset(&prClientAddr, 0, sizeof(prClientAddr));
  166. clientFD = PR_Accept(mFD, &prClientAddr, PR_INTERVAL_NO_WAIT);
  167. PRNetAddrToNetAddr(&prClientAddr, &clientAddr);
  168. if (!clientFD) {
  169. NS_WARNING("PR_Accept failed");
  170. mCondition = NS_ERROR_UNEXPECTED;
  171. return;
  172. }
  173. // Accept succeeded, create socket transport and notify consumer
  174. CreateClientTransport(clientFD, clientAddr);
  175. }
  176. void
  177. nsServerSocket::OnSocketDetached(PRFileDesc *fd)
  178. {
  179. // force a failure condition if none set; maybe the STS is shutting down :-/
  180. if (NS_SUCCEEDED(mCondition))
  181. mCondition = NS_ERROR_ABORT;
  182. if (mFD)
  183. {
  184. NS_ASSERTION(mFD == fd, "wrong file descriptor");
  185. PR_Close(mFD);
  186. mFD = nullptr;
  187. }
  188. if (mListener)
  189. {
  190. mListener->OnStopListening(this, mCondition);
  191. // need to atomically clear mListener. see our Close() method.
  192. RefPtr<nsIServerSocketListener> listener = nullptr;
  193. {
  194. MutexAutoLock lock(mLock);
  195. listener = mListener.forget();
  196. }
  197. // XXX we need to proxy the release to the listener's target thread to work
  198. // around bug 337492.
  199. if (listener) {
  200. NS_ProxyRelease(mListenerTarget, listener.forget());
  201. }
  202. }
  203. }
  204. void
  205. nsServerSocket::IsLocal(bool *aIsLocal)
  206. {
  207. #if defined(XP_UNIX)
  208. // Unix-domain sockets are always local.
  209. if (mAddr.raw.family == PR_AF_LOCAL)
  210. {
  211. *aIsLocal = true;
  212. return;
  213. }
  214. #endif
  215. // If bound to loopback, this server socket only accepts local connections.
  216. *aIsLocal = PR_IsNetAddrType(&mAddr, PR_IpAddrLoopback);
  217. }
  218. void
  219. nsServerSocket::KeepWhenOffline(bool *aKeepWhenOffline)
  220. {
  221. *aKeepWhenOffline = mKeepWhenOffline;
  222. }
  223. //-----------------------------------------------------------------------------
  224. // nsServerSocket::nsISupports
  225. //-----------------------------------------------------------------------------
  226. NS_IMPL_ISUPPORTS(nsServerSocket, nsIServerSocket)
  227. //-----------------------------------------------------------------------------
  228. // nsServerSocket::nsIServerSocket
  229. //-----------------------------------------------------------------------------
  230. NS_IMETHODIMP
  231. nsServerSocket::Init(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog)
  232. {
  233. return InitSpecialConnection(aPort, aLoopbackOnly ? LoopbackOnly : 0, aBackLog);
  234. }
  235. NS_IMETHODIMP
  236. nsServerSocket::InitWithFilename(nsIFile *aPath, uint32_t aPermissions, int32_t aBacklog)
  237. {
  238. #if defined(XP_UNIX)
  239. nsresult rv;
  240. nsAutoCString path;
  241. rv = aPath->GetNativePath(path);
  242. if (NS_FAILED(rv))
  243. return rv;
  244. // Create a Unix domain PRNetAddr referring to the given path.
  245. PRNetAddr addr;
  246. if (path.Length() > sizeof(addr.local.path) - 1)
  247. return NS_ERROR_FILE_NAME_TOO_LONG;
  248. addr.local.family = PR_AF_LOCAL;
  249. memcpy(addr.local.path, path.get(), path.Length());
  250. addr.local.path[path.Length()] = '\0';
  251. rv = InitWithAddress(&addr, aBacklog);
  252. if (NS_FAILED(rv))
  253. return rv;
  254. return aPath->SetPermissions(aPermissions);
  255. #else
  256. return NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED;
  257. #endif
  258. }
  259. NS_IMETHODIMP
  260. nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags,
  261. int32_t aBackLog)
  262. {
  263. PRNetAddrValue val;
  264. PRNetAddr addr;
  265. if (aPort < 0)
  266. aPort = 0;
  267. if (aFlags & nsIServerSocket::LoopbackOnly)
  268. val = PR_IpAddrLoopback;
  269. else
  270. val = PR_IpAddrAny;
  271. PR_SetNetAddr(val, PR_AF_INET, aPort, &addr);
  272. mKeepWhenOffline = ((aFlags & nsIServerSocket::KeepWhenOffline) != 0);
  273. return InitWithAddress(&addr, aBackLog);
  274. }
  275. NS_IMETHODIMP
  276. nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog)
  277. {
  278. NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
  279. nsresult rv;
  280. //
  281. // configure listening socket...
  282. //
  283. mFD = PR_OpenTCPSocket(aAddr->raw.family);
  284. if (!mFD)
  285. {
  286. NS_WARNING("unable to create server socket");
  287. return ErrorAccordingToNSPR(PR_GetError());
  288. }
  289. PRSocketOptionData opt;
  290. opt.option = PR_SockOpt_Reuseaddr;
  291. opt.value.reuse_addr = true;
  292. PR_SetSocketOption(mFD, &opt);
  293. opt.option = PR_SockOpt_Nonblocking;
  294. opt.value.non_blocking = true;
  295. PR_SetSocketOption(mFD, &opt);
  296. if (PR_Bind(mFD, aAddr) != PR_SUCCESS)
  297. {
  298. NS_WARNING("failed to bind socket");
  299. goto fail;
  300. }
  301. if (aBackLog < 0)
  302. aBackLog = 5; // seems like a reasonable default
  303. if (PR_Listen(mFD, aBackLog) != PR_SUCCESS)
  304. {
  305. NS_WARNING("cannot listen on socket");
  306. goto fail;
  307. }
  308. // get the resulting socket address, which may be different than what
  309. // we passed to bind.
  310. if (PR_GetSockName(mFD, &mAddr) != PR_SUCCESS)
  311. {
  312. NS_WARNING("cannot get socket name");
  313. goto fail;
  314. }
  315. // Set any additional socket defaults needed by child classes
  316. rv = SetSocketDefaults();
  317. if (NS_WARN_IF(NS_FAILED(rv))) {
  318. goto fail;
  319. }
  320. // wait until AsyncListen is called before polling the socket for
  321. // client connections.
  322. return NS_OK;
  323. fail:
  324. rv = ErrorAccordingToNSPR(PR_GetError());
  325. Close();
  326. return rv;
  327. }
  328. NS_IMETHODIMP
  329. nsServerSocket::Close()
  330. {
  331. {
  332. MutexAutoLock lock(mLock);
  333. // we want to proxy the close operation to the socket thread if a listener
  334. // has been set. otherwise, we should just close the socket here...
  335. if (!mListener)
  336. {
  337. if (mFD)
  338. {
  339. PR_Close(mFD);
  340. mFD = nullptr;
  341. }
  342. return NS_OK;
  343. }
  344. }
  345. return PostEvent(this, &nsServerSocket::OnMsgClose);
  346. }
  347. namespace {
  348. class ServerSocketListenerProxy final : public nsIServerSocketListener
  349. {
  350. ~ServerSocketListenerProxy() {}
  351. public:
  352. explicit ServerSocketListenerProxy(nsIServerSocketListener* aListener)
  353. : mListener(new nsMainThreadPtrHolder<nsIServerSocketListener>(aListener))
  354. , mTargetThread(do_GetCurrentThread())
  355. { }
  356. NS_DECL_THREADSAFE_ISUPPORTS
  357. NS_DECL_NSISERVERSOCKETLISTENER
  358. class OnSocketAcceptedRunnable : public Runnable
  359. {
  360. public:
  361. OnSocketAcceptedRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener,
  362. nsIServerSocket* aServ,
  363. nsISocketTransport* aTransport)
  364. : mListener(aListener)
  365. , mServ(aServ)
  366. , mTransport(aTransport)
  367. { }
  368. NS_DECL_NSIRUNNABLE
  369. private:
  370. nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
  371. nsCOMPtr<nsIServerSocket> mServ;
  372. nsCOMPtr<nsISocketTransport> mTransport;
  373. };
  374. class OnStopListeningRunnable : public Runnable
  375. {
  376. public:
  377. OnStopListeningRunnable(const nsMainThreadPtrHandle<nsIServerSocketListener>& aListener,
  378. nsIServerSocket* aServ,
  379. nsresult aStatus)
  380. : mListener(aListener)
  381. , mServ(aServ)
  382. , mStatus(aStatus)
  383. { }
  384. NS_DECL_NSIRUNNABLE
  385. private:
  386. nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
  387. nsCOMPtr<nsIServerSocket> mServ;
  388. nsresult mStatus;
  389. };
  390. private:
  391. nsMainThreadPtrHandle<nsIServerSocketListener> mListener;
  392. nsCOMPtr<nsIEventTarget> mTargetThread;
  393. };
  394. NS_IMPL_ISUPPORTS(ServerSocketListenerProxy,
  395. nsIServerSocketListener)
  396. NS_IMETHODIMP
  397. ServerSocketListenerProxy::OnSocketAccepted(nsIServerSocket* aServ,
  398. nsISocketTransport* aTransport)
  399. {
  400. RefPtr<OnSocketAcceptedRunnable> r =
  401. new OnSocketAcceptedRunnable(mListener, aServ, aTransport);
  402. return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
  403. }
  404. NS_IMETHODIMP
  405. ServerSocketListenerProxy::OnStopListening(nsIServerSocket* aServ,
  406. nsresult aStatus)
  407. {
  408. RefPtr<OnStopListeningRunnable> r =
  409. new OnStopListeningRunnable(mListener, aServ, aStatus);
  410. return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
  411. }
  412. NS_IMETHODIMP
  413. ServerSocketListenerProxy::OnSocketAcceptedRunnable::Run()
  414. {
  415. mListener->OnSocketAccepted(mServ, mTransport);
  416. return NS_OK;
  417. }
  418. NS_IMETHODIMP
  419. ServerSocketListenerProxy::OnStopListeningRunnable::Run()
  420. {
  421. mListener->OnStopListening(mServ, mStatus);
  422. return NS_OK;
  423. }
  424. } // namespace
  425. NS_IMETHODIMP
  426. nsServerSocket::AsyncListen(nsIServerSocketListener *aListener)
  427. {
  428. // ensuring mFD implies ensuring mLock
  429. NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
  430. NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
  431. {
  432. MutexAutoLock lock(mLock);
  433. mListener = new ServerSocketListenerProxy(aListener);
  434. mListenerTarget = NS_GetCurrentThread();
  435. }
  436. // Child classes may need to do additional setup just before listening begins
  437. nsresult rv = OnSocketListen();
  438. if (NS_WARN_IF(NS_FAILED(rv))) {
  439. return rv;
  440. }
  441. return PostEvent(this, &nsServerSocket::OnMsgAttach);
  442. }
  443. NS_IMETHODIMP
  444. nsServerSocket::GetPort(int32_t *aResult)
  445. {
  446. // no need to enter the lock here
  447. uint16_t port;
  448. if (mAddr.raw.family == PR_AF_INET)
  449. port = mAddr.inet.port;
  450. else if (mAddr.raw.family == PR_AF_INET6)
  451. port = mAddr.ipv6.port;
  452. else
  453. return NS_ERROR_FAILURE;
  454. *aResult = static_cast<int32_t>(NetworkEndian::readUint16(&port));
  455. return NS_OK;
  456. }
  457. NS_IMETHODIMP
  458. nsServerSocket::GetAddress(PRNetAddr *aResult)
  459. {
  460. // no need to enter the lock here
  461. memcpy(aResult, &mAddr, sizeof(mAddr));
  462. return NS_OK;
  463. }
  464. } // namespace net
  465. } // namespace mozilla