PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/dgram.js

https://gitlab.com/GeekSir/node
JavaScript | 487 lines | 335 code | 112 blank | 40 comment | 79 complexity | 3ec6d2f53ad161ec72075c52efbf60e5 MD5 | raw file
Possible License(s): 0BSD, Apache-2.0, MPL-2.0-no-copyleft-exception, JSON, WTFPL, CC-BY-SA-3.0, Unlicense, ISC, BSD-3-Clause, MIT, AGPL-3.0
  1. // Copyright Joyent, Inc. and other Node contributors.
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a
  4. // copy of this software and associated documentation files (the
  5. // "Software"), to deal in the Software without restriction, including
  6. // without limitation the rights to use, copy, modify, merge, publish,
  7. // distribute, sublicense, and/or sell copies of the Software, and to permit
  8. // persons to whom the Software is furnished to do so, subject to the
  9. // following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included
  12. // in all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  15. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  17. // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  18. // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  19. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  20. // USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. 'use strict';
  22. var assert = require('assert');
  23. var util = require('util');
  24. var events = require('events');
  25. var constants = require('constants');
  26. var Buffer = require('buffer').Buffer;
  27. var UDP = process.binding('udp_wrap').UDP;
  28. var SendWrap = process.binding('udp_wrap').SendWrap;
  29. var BIND_STATE_UNBOUND = 0;
  30. var BIND_STATE_BINDING = 1;
  31. var BIND_STATE_BOUND = 2;
  32. // lazily loaded
  33. var cluster = null;
  34. var dns = null;
  35. var errnoException = util._errnoException;
  36. function lookup(address, family, callback) {
  37. if (!dns)
  38. dns = require('dns');
  39. return dns.lookup(address, family, callback);
  40. }
  41. function lookup4(address, callback) {
  42. return lookup(address || '0.0.0.0', 4, callback);
  43. }
  44. function lookup6(address, callback) {
  45. return lookup(address || '::0', 6, callback);
  46. }
  47. function newHandle(type) {
  48. if (type == 'udp4') {
  49. var handle = new UDP;
  50. handle.lookup = lookup4;
  51. return handle;
  52. }
  53. if (type == 'udp6') {
  54. var handle = new UDP;
  55. handle.lookup = lookup6;
  56. handle.bind = handle.bind6;
  57. handle.send = handle.send6;
  58. return handle;
  59. }
  60. if (type == 'unix_dgram')
  61. throw new Error('unix_dgram sockets are not supported any more.');
  62. throw new Error('Bad socket type specified. Valid types are: udp4, udp6');
  63. }
  64. exports._createSocketHandle = function(address, port, addressType, fd) {
  65. // Opening an existing fd is not supported for UDP handles.
  66. assert(!util.isNumber(fd) || fd < 0);
  67. var handle = newHandle(addressType);
  68. if (port || address) {
  69. var err = handle.bind(address, port || 0, 0);
  70. if (err) {
  71. handle.close();
  72. return err;
  73. }
  74. }
  75. return handle;
  76. };
  77. function Socket(type, listener) {
  78. events.EventEmitter.call(this);
  79. if (typeof type === 'object') {
  80. var options = type;
  81. type = options.type;
  82. }
  83. var handle = newHandle(type);
  84. handle.owner = this;
  85. this._handle = handle;
  86. this._receiving = false;
  87. this._bindState = BIND_STATE_UNBOUND;
  88. this.type = type;
  89. this.fd = null; // compatibility hack
  90. // If true - UV_UDP_REUSEADDR flag will be set
  91. this._reuseAddr = options && options.reuseAddr;
  92. if (util.isFunction(listener))
  93. this.on('message', listener);
  94. }
  95. util.inherits(Socket, events.EventEmitter);
  96. exports.Socket = Socket;
  97. exports.createSocket = function(type, listener) {
  98. return new Socket(type, listener);
  99. };
  100. function startListening(socket) {
  101. socket._handle.onmessage = onMessage;
  102. // Todo: handle errors
  103. socket._handle.recvStart();
  104. socket._receiving = true;
  105. socket._bindState = BIND_STATE_BOUND;
  106. socket.fd = -42; // compatibility hack
  107. socket.emit('listening');
  108. }
  109. function replaceHandle(self, newHandle) {
  110. // Set up the handle that we got from master.
  111. newHandle.lookup = self._handle.lookup;
  112. newHandle.bind = self._handle.bind;
  113. newHandle.send = self._handle.send;
  114. newHandle.owner = self;
  115. // Replace the existing handle by the handle we got from master.
  116. self._handle.close();
  117. self._handle = newHandle;
  118. }
  119. Socket.prototype.bind = function(port /*, address, callback*/) {
  120. var self = this;
  121. self._healthCheck();
  122. if (this._bindState != BIND_STATE_UNBOUND)
  123. throw new Error('Socket is already bound');
  124. this._bindState = BIND_STATE_BINDING;
  125. if (util.isFunction(arguments[arguments.length - 1]))
  126. self.once('listening', arguments[arguments.length - 1]);
  127. var UDP = process.binding('udp_wrap').UDP;
  128. if (port instanceof UDP) {
  129. replaceHandle(self, port);
  130. startListening(self);
  131. return;
  132. }
  133. var address;
  134. var exclusive;
  135. if (util.isObject(port)) {
  136. address = port.address || '';
  137. exclusive = !!port.exclusive;
  138. port = port.port;
  139. } else {
  140. address = util.isFunction(arguments[1]) ? '' : arguments[1];
  141. exclusive = false;
  142. }
  143. // resolve address first
  144. self._handle.lookup(address, function(err, ip) {
  145. if (err) {
  146. self._bindState = BIND_STATE_UNBOUND;
  147. self.emit('error', err);
  148. return;
  149. }
  150. if (!cluster)
  151. cluster = require('cluster');
  152. if (cluster.isWorker && !exclusive) {
  153. cluster._getServer(self, ip, port, self.type, -1, function(err, handle) {
  154. if (err) {
  155. self.emit('error', errnoException(err, 'bind'));
  156. self._bindState = BIND_STATE_UNBOUND;
  157. return;
  158. }
  159. if (!self._handle)
  160. // handle has been closed in the mean time.
  161. return handle.close();
  162. replaceHandle(self, handle);
  163. startListening(self);
  164. });
  165. } else {
  166. if (!self._handle)
  167. return; // handle has been closed in the mean time
  168. var flags = 0;
  169. if (self._reuseAddr)
  170. flags |= constants.UV_UDP_REUSEADDR;
  171. var err = self._handle.bind(ip, port || 0, flags);
  172. if (err) {
  173. self.emit('error', errnoException(err, 'bind'));
  174. self._bindState = BIND_STATE_UNBOUND;
  175. // Todo: close?
  176. return;
  177. }
  178. startListening(self);
  179. }
  180. });
  181. };
  182. // thin wrapper around `send`, here for compatibility with dgram_legacy.js
  183. Socket.prototype.sendto = function(buffer,
  184. offset,
  185. length,
  186. port,
  187. address,
  188. callback) {
  189. if (!util.isNumber(offset) || !util.isNumber(length))
  190. throw new Error('send takes offset and length as args 2 and 3');
  191. if (!util.isString(address))
  192. throw new Error(this.type + ' sockets must send to port, address');
  193. this.send(buffer, offset, length, port, address, callback);
  194. };
  195. Socket.prototype.send = function(buffer,
  196. offset,
  197. length,
  198. port,
  199. address,
  200. callback) {
  201. var self = this;
  202. if (util.isString(buffer))
  203. buffer = new Buffer(buffer);
  204. if (!util.isBuffer(buffer))
  205. throw new TypeError('First argument must be a buffer or string.');
  206. offset = offset | 0;
  207. if (offset < 0)
  208. throw new RangeError('Offset should be >= 0');
  209. if ((length == 0 && offset > buffer.length) ||
  210. (length > 0 && offset >= buffer.length))
  211. throw new RangeError('Offset into buffer too large');
  212. // Sending a zero-length datagram is kind of pointless but it _is_
  213. // allowed, hence check that length >= 0 rather than > 0.
  214. length = length | 0;
  215. if (length < 0)
  216. throw new RangeError('Length should be >= 0');
  217. if (offset + length > buffer.length)
  218. throw new RangeError('Offset + length beyond buffer length');
  219. port = port | 0;
  220. if (port <= 0 || port > 65535)
  221. throw new RangeError('Port should be > 0 and < 65536');
  222. // Normalize callback so it's either a function or undefined but not anything
  223. // else.
  224. if (!util.isFunction(callback))
  225. callback = undefined;
  226. self._healthCheck();
  227. if (self._bindState == BIND_STATE_UNBOUND)
  228. self.bind({port: 0, exclusive: true}, null);
  229. // If the socket hasn't been bound yet, push the outbound packet onto the
  230. // send queue and send after binding is complete.
  231. if (self._bindState != BIND_STATE_BOUND) {
  232. // If the send queue hasn't been initialized yet, do it, and install an
  233. // event handler that flushes the send queue after binding is done.
  234. if (!self._sendQueue) {
  235. self._sendQueue = [];
  236. self.once('listening', function() {
  237. // Flush the send queue.
  238. for (var i = 0; i < self._sendQueue.length; i++)
  239. self.send.apply(self, self._sendQueue[i]);
  240. self._sendQueue = undefined;
  241. });
  242. }
  243. self._sendQueue.push([buffer, offset, length, port, address, callback]);
  244. return;
  245. }
  246. self._handle.lookup(address, function(ex, ip) {
  247. if (ex) {
  248. if (callback) callback(ex);
  249. self.emit('error', ex);
  250. }
  251. else if (self._handle) {
  252. var req = new SendWrap();
  253. req.buffer = buffer; // Keep reference alive.
  254. req.length = length;
  255. if (callback) {
  256. req.callback = callback;
  257. req.oncomplete = afterSend;
  258. }
  259. var err = self._handle.send(req,
  260. buffer,
  261. offset,
  262. length,
  263. port,
  264. ip,
  265. !!callback);
  266. if (err && callback) {
  267. // don't emit as error, dgram_legacy.js compatibility
  268. process.nextTick(function() {
  269. callback(errnoException(err, 'send'));
  270. });
  271. }
  272. }
  273. });
  274. };
  275. function afterSend(err) {
  276. this.callback(err ? errnoException(err, 'send') : null, this.length);
  277. }
  278. Socket.prototype.close = function() {
  279. this._healthCheck();
  280. this._stopReceiving();
  281. this._handle.close();
  282. this._handle = null;
  283. this.emit('close');
  284. };
  285. Socket.prototype.address = function() {
  286. this._healthCheck();
  287. var out = {};
  288. var err = this._handle.getsockname(out);
  289. if (err) {
  290. throw errnoException(err, 'getsockname');
  291. }
  292. return out;
  293. };
  294. Socket.prototype.setBroadcast = function(arg) {
  295. var err = this._handle.setBroadcast(arg ? 1 : 0);
  296. if (err) {
  297. throw errnoException(err, 'setBroadcast');
  298. }
  299. };
  300. Socket.prototype.setTTL = function(arg) {
  301. if (!util.isNumber(arg)) {
  302. throw new TypeError('Argument must be a number');
  303. }
  304. var err = this._handle.setTTL(arg);
  305. if (err) {
  306. throw errnoException(err, 'setTTL');
  307. }
  308. return arg;
  309. };
  310. Socket.prototype.setMulticastTTL = function(arg) {
  311. if (!util.isNumber(arg)) {
  312. throw new TypeError('Argument must be a number');
  313. }
  314. var err = this._handle.setMulticastTTL(arg);
  315. if (err) {
  316. throw errnoException(err, 'setMulticastTTL');
  317. }
  318. return arg;
  319. };
  320. Socket.prototype.setMulticastLoopback = function(arg) {
  321. var err = this._handle.setMulticastLoopback(arg ? 1 : 0);
  322. if (err) {
  323. throw errnoException(err, 'setMulticastLoopback');
  324. }
  325. return arg; // 0.4 compatibility
  326. };
  327. Socket.prototype.addMembership = function(multicastAddress,
  328. interfaceAddress) {
  329. this._healthCheck();
  330. if (!multicastAddress) {
  331. throw new Error('multicast address must be specified');
  332. }
  333. var err = this._handle.addMembership(multicastAddress, interfaceAddress);
  334. if (err) {
  335. throw errnoException(err, 'addMembership');
  336. }
  337. };
  338. Socket.prototype.dropMembership = function(multicastAddress,
  339. interfaceAddress) {
  340. this._healthCheck();
  341. if (!multicastAddress) {
  342. throw new Error('multicast address must be specified');
  343. }
  344. var err = this._handle.dropMembership(multicastAddress, interfaceAddress);
  345. if (err) {
  346. throw errnoException(err, 'dropMembership');
  347. }
  348. };
  349. Socket.prototype._healthCheck = function() {
  350. if (!this._handle)
  351. throw new Error('Not running'); // error message from dgram_legacy.js
  352. };
  353. Socket.prototype._stopReceiving = function() {
  354. if (!this._receiving)
  355. return;
  356. this._handle.recvStop();
  357. this._receiving = false;
  358. this.fd = null; // compatibility hack
  359. };
  360. function onMessage(nread, handle, buf, rinfo) {
  361. var self = handle.owner;
  362. if (nread < 0) {
  363. return self.emit('error', errnoException(nread, 'recvmsg'));
  364. }
  365. rinfo.size = buf.length; // compatibility
  366. self.emit('message', buf, rinfo);
  367. }
  368. Socket.prototype.ref = function() {
  369. if (this._handle)
  370. this._handle.ref();
  371. };
  372. Socket.prototype.unref = function() {
  373. if (this._handle)
  374. this._handle.unref();
  375. };