PageRenderTime 6ms CodeModel.GetById 4ms app.highlight 44ms RepoModel.GetById 1ms app.codeStats 0ms

/index.js

https://github.com/bigeasy/udt
JavaScript | 859 lines | 608 code | 137 blank | 114 comment | 144 complexity | cc1d2361b5d4672a0b0a3084bd725f0e MD5 | raw file
  1var dgram = require('dgram'),
  2    socket = dgram.createSocket('udp4'),
  3    packet = require('packet'),
  4    common = require('./common'),
  5    crypto = require('crypto'),
  6    dns = require('dns'),
  7    __slice = [].slice;
  8
  9const CONTROL_TYPES = 'handshake keep-alive acknowledgement'.split(/\s+/);
 10const MAX_MSG_NO = 0x1FFFFFFF;
 11const MAX_SEQ_NO = Math.pow(2, 31) - 1;
 12const IMMEDIATE = [ 0, 0 ];
 13
 14var socketId = crypto.randomBytes(4).readUInt32BE(0);
 15
 16function nextSocketId () {
 17  if (socketId == 1) socketId = Math.pow(2, 32);
 18  return --socketId;
 19}
 20
 21var Stream = require('stream');
 22var util = require('util');
 23var events = require('events');
 24var net = require('net');
 25
 26// The start of time used in our high resolution timings.
 27var epoch = process.hrtime();
 28
 29var heapIndexKeyCounter = 0;
 30
 31function Heap (before) {
 32  this.array = [];
 33  this.indexKey = '_Heap_index_key_' + (heapIndexKeyCounter++);
 34  this.before = before;
 35}
 36
 37Heap.prototype.__defineGetter__("length", function () {
 38  return this.array.length;
 39});
 40
 41Heap.prototype.peek = function () {
 42  return this.array[0];
 43}
 44
 45Heap.prototype.bubbleUp = function (index) {
 46  var before = this.before, array = this.array, indexKey = this.indexKey, node = array[index]; 
 47  while (index > 0) {
 48    var parent = index - 1 >> 1;
 49    if (before(node, array[parent])) {
 50      array[index] = array[parent];
 51      array[parent] = node;
 52      array[index][indexKey] = index;
 53      array[parent][indexKey] = parent;
 54      index = parent;
 55    } else {
 56      break;
 57    }
 58  }
 59}
 60
 61Heap.prototype.sinkDown = function (index) {
 62  var array = this.array, indexKey = this.indexKey,
 63      length = array.length, node = array[index],
 64      left, right, child;
 65  for (left = index * 2 + 1; left < length; l = index * 2 + 1) {
 66    child = left;
 67    right = left + 1;
 68    if (right < length && before(array[right], array[left])) {
 69      child = right;
 70    }
 71    if (before(array[child][indexKey], node[indexKey])) {
 72      array[index] = array[child];
 73      array[child] = node;
 74      array[index][indexKey] = index;
 75      array[child][indexKey] = child;
 76      index = child;
 77    } else {
 78      break;
 79    }
 80  }
 81}
 82
 83Heap.prototype.remove = function (node) {
 84  var array = this.array, indexKey = this.indexKey, last = array.pop(), index = node[indexKey];
 85  if (index != array.length) {
 86    array[index] = last;
 87    if (less(end, node)) {
 88      this.bubbleUp(index);
 89    } else {
 90      this.sinkDown(index);
 91    }
 92  }
 93  delete node[indexKey];
 94}
 95
 96Heap.prototype.push = function (node, value) {
 97  var array = this.array, indexKey = this.indexKey, index = array.length;
 98  if (node[indexKey] != null) {
 99    this.remove(node);
100    this.push(node);
101  } else {
102    array.push(node);
103    node[indexKey] = index;
104    this.bubbleUp(index);
105  }
106}
107
108Heap.prototype.pop = function (node) {
109  var array = this.array, indexKey = this.indexKey, result = array[0], last = array.pop();
110  if (array.length) {
111    array[0] = last;
112    last[indexKey] = 0; 
113    this.sinkDown(0);
114  }
115  delete result[indexKey];
116  return result;
117}
118
119// Comparison operator generator for high-resolution time for use with heap.
120function sooner (property) {
121  return function (a, b) {
122    if (a[property][0] < b[property][0]) return true;
123    if (a[property][0] > b[property][0]) return false;
124    return a[property][1] < b[property][1];
125  }
126}
127
128function die () {
129  console.log.apply(console, __slice.call(arguments, 0));
130  return process.exit(1);
131}
132
133function say () { return console.log.apply(console, __slice.call(arguments, 0)) }
134
135function extend (to, from) {
136  for (var key in from) to[key] = from[key];
137  return to;
138}
139
140function parseDotDecimal (quad) {
141  quad = quad.split('.');
142  for (var i = 3, address = 0; i >= 0; i--) {
143    address = address + quad[i] * Math.pow(256, i);
144  }
145  return address;
146}
147
148// Native control algorithm is an event emitter with certain properties. Ought
149// to be simple enough for the user to implement a native control algorithm as
150// an event emitter.
151function NativeControlAlgorithm () {
152  this.roundTripTime = 0;
153  this.maximumSegmentSize = 0;
154  this.estimatedBandwidth = 0;
155  this.latestPacketSequenceNo = 0;
156  this.windowSize = 16;
157}
158util.inherits(NativeControlAlgorithm, events.EventEmitter);
159
160var sendQueue = new (function () {
161  var before = sooner('_sendTime'), queue = new Heap(before), sending = false;
162  function enqueue (socket, packet, when) {
163    queue.add({ socket: socket, packet: packet, when: when });
164    if (!sending) poll();
165  }
166  function schedule (socket, timestamp) {
167    // This gave me a funny feeling, one of violating encapsulation by using a
168    // property in the socket object from the send queue, except that am I
169    // supposed to do? This is what I would have called violating encapsulation
170    // in my Java days, it triggers the creation of a dozen new types to
171    // preserve encapsulation. I've yet to completely de-program myself of this
172    // sort of rote programming. The send queue is within the same capsule as
173    // the socket. They are interdependent. They existing for each other. The
174    // socket object's underscored properties are part of its implementation, in
175    // fact, the socket is not the implementation, the whole API is.
176    socket._sendTime = timestamp;
177    queue.push(socket);
178    if (!sending) poll();
179  }
180  function poll () {
181    sending = true;
182    if (! queue.length) {
183      sending = false;
184    } else {
185      send();
186    }
187  }
188  function send () {
189    var socket;
190    if (before(queue.peek(), { _sendTime: process.hrtime() })) {
191      socket = queue.pop();
192      socket._endPoint.transmit(socket);
193    }
194    process.nextTick(poll);
195  }
196  extend(this, { schedule: schedule });
197})();
198
199function Socket (options) {
200  if (!(this instanceof Socket)) return new Socket();
201
202  if (options === void(0)) options = {};
203
204  Stream.call(this);
205
206  this._socketId = nextSocketId();
207  this._messageNumber = 1;
208  this._flowWindowSize = 0;
209  this._ccc = options.ccc || new NativeControlAlgorithm;
210  this._packet = new Buffer(1500);
211  this._pending = [];
212  this._sent = [[]];
213}
214util.inherits(Socket, Stream);
215
216exports.Socket = Socket;
217
218function exceptional () { return new Error(); }
219
220// Wrapper around an underlying UDP datagram socket.
221function EndPoint (local) {
222  this.listeners = 0;
223  this.dgram = dgram.createSocket('udp4');
224  this.dgram.on('message', EndPoint.prototype.receive.bind(this));
225  this.dgram.bind(local.port, local.address);
226  this.local = this.dgram.address();
227  this.packet = new Buffer(2048);
228  this.sockets = {};
229}
230EndPoint.prototype.shakeHands = function (socket) {
231  // Stash the socket so we can track it by the socket identifier.
232  this.sockets[socket._socketId] = socket;
233
234  // Start of client handshake.
235  socket._status = "syn";
236
237  // Send a handshake. Use hard-coded defaults for packet and window size.
238  this.sendHandshake(socket,
239  { control: 1
240  , type: 0
241  , additional: 0
242  , timestamp: 0
243  , destination: 0
244  , version: 4
245  , socketType: 1
246  , sequence: socket._sequence
247  , maxPacketSize: 1500
248  , windowSize: 8192
249  , connectionType: 1
250  , socketId: socket._socketId
251  , synCookie: 0
252  , address: parseDotDecimal(socket._peer.address)
253  });
254}
255EndPoint.prototype.control = function (socket, pattern, message, callback) {
256  var serializer = common.serializer, dgram = this.dgram, packet = new Buffer(64), peer = socket._peer;
257
258  message.control = 1;
259  message.destination = peer.socketId;
260  // TODO: Implement timestamp.
261  message.timestamp = 0;
262
263  // Format a shutdown packet, simply a header packet of type shutdown.
264  serializer.reset();
265  serializer.serialize(pattern, message);
266  serializer.write(packet);
267
268  dgram.send(packet, 0, serializer.length, peer.port, peer.address, callback);
269}
270EndPoint.prototype.shutdown = function (socket, send) {
271  // Remove the socket from the stash.
272  delete this.sockets[socket._socketId];
273
274  // Zero the status.
275  delete socket._status;
276
277  var endPoint = this, dgram = endPoint.dgram;
278
279  if (send) {
280    var serializer = common.serializer, packet = endPoint.packet, peer = socket._peer;
281
282    // Format a shutdown packet, simply a header packet of type shutdown.
283    serializer.reset();
284    serializer.serialize('header', 
285    { control: 1
286    , type: 0x5
287    , additional: 0
288    , timestamp: 0
289    , destination: peer.socketId
290    });
291    serializer.write(packet);
292
293    dgram.send(packet, 0, serializer.length, peer.port, peer.address, finalize);
294  } else {
295    finalize();
296  }
297  
298  function finalize () {
299    // If we were a bound listening socket, see if we ought to close.
300    if (socket._listener && !--endPoint.listeners && endPoint.server._closing) {
301      // This will unassign `endPoint.server`.
302      endPoint.server.close();
303    }
304    // Dispose of the end point and UDP socket if it is no longer referenced.
305    if (Object.keys(endPoint.sockets).length == 0) {
306      delete endPoints[endPoint.local.port][endPoint.local.address];
307      if (Object.keys(endPoints[endPoint.local.port]).length == 0) {
308        delete endPoints[endPoint.local.port];
309      }
310      dgram.close();
311    }
312  }
313}
314// Send the handshake four times a second until we get a response, or until four
315// seconds is up.
316EndPoint.prototype.sendHandshake = function (socket, handshake) {
317  var endPoint = this, count = 0, peer = socket._peer;
318  socket._handshakeInterval = setInterval(function () {
319    if (++count == 12) {
320      clearInterval(socket._handshakeInterval);
321      socket.emit('error', new Error('connection timeout'));
322    } else {
323      endPoint.send('handshake', handshake, socket._peer);
324    }
325  }, 250);
326}
327EndPoint.prototype.send = function (packetType, object, peer) {
328  var serializer = common.serializer,
329      packet = this.packet,
330      dgram = this.dgram;
331  
332  serializer.reset();
333  serializer.serialize(packetType, object);
334  serializer.write(packet);
335
336  dgram.send(packet, 0, serializer.length, peer.port, peer.address);
337}
338EndPoint.prototype.receive = function (msg, rinfo) {
339  var endPoint = this, parser = common.parser, handler;
340  parser.reset();
341  parser.extract('header', function (header) {
342    header.rinfo = rinfo;
343    header.length = msg.length;
344    if (header.control) {
345      if (header.destination) {
346        // TODO: Socket not found...
347        var socket = endPoint.sockets[header.destination];
348        switch (header.type) {
349        // Keep-alive.
350        case 0x1:
351          break;
352        // Shutdown.
353        case 0x5:
354          endPoint.shutdown(socket, false);
355          break;
356        // Notifications from Bill the Cat. (Ack-ack.)
357        case 0x6:
358          break;
359        default:
360          var name = CONTROL_TYPES[header.type];
361          console.log(name, header);
362          parser.extract(name, endPoint[name].bind(endPoint, parser, socket, header))
363        }
364      // Hmm... Do you explicitly enable rendezvous?
365      } else if (header.type == 0 && endPoint.server) {
366        parser.extract('handshake', endPoint.connect.bind(endPoint, rinfo, header))
367      }
368    } else {
369    }
370  });
371  parser.parse(msg);
372}
373EndPoint.prototype.handshake = function (parser, socket, header, handshake) {
374  switch (socket._status) {
375  case 'syn':
376    // Only respond to an initial handshake.
377    if (handshake.connectionType != 1) break;
378
379    clearInterval(socket._handshakeInterval);
380
381    socket._status = 'syn-ack';
382
383    // Unify the packet object for serialization.
384    handshake = extend(handshake, header);
385
386    // Set the destination to nothing.
387    handshake.destination = 0;
388
389    // Select the lesser of the negotiated values.
390    // TODO: Constants are a bad thing...
391    handshake.maxPacketSize = Math.min(handshake.maxPacketSize, 1500);
392    handshake.windowSize = Math.min(handshake.windowSize, 8192);
393    handshake.connectionType = -1;
394
395    this.sendHandshake(socket, handshake);
396    break;
397  case 'syn-ack':
398    // Only respond to an follow-up handshake.
399    if (handshake.connectionType != -1) break;
400
401    clearInterval(socket._handshakeInterval);
402
403    socket._status = 'connected';
404    socket._handshake = handshake;
405    socket._peer.socketId = handshake.socketId;
406
407    socket.emit('connect');
408    break;
409  }
410}
411
412// Binary search, implemented, as always, by taking a [peek at
413// Sedgewick](http://algs4.cs.princeton.edu/11model/BinarySearch.java.html).
414function binarySearch (comparator, array, key) {
415  var low = 0, high = array.length - 1, partition, compare;
416  while (low <= high) {
417    partition = Math.floor(low + (high - low) / 2);
418    compare = comparator(key, array[partition]);
419    if (compare < 0) high = partition - 1;
420    else if (compare > 0) low = partition + 1;
421    else return partition;
422  }
423  return low;
424}
425
426// Compare two objects by their sequence property.
427function bySequence (left, right) { return left.sequence - right.sequence }
428
429EndPoint.prototype.acknowledgement = function (parser, socket, header, ack) {
430  // All parsing in one fell swoop so we don't do something that causes a next
431  // tick which might cause the parser to be reused.
432  if (header.length == 40) {
433    parser.extract('statistics', this.fullAcknowledgement.bind(this, socket, header, ack));
434  } else {
435    this.lightAcknowledgement(socket, header, ack);
436  }
437};
438
439// Remove the sent packets that have been received.
440EndPoint.prototype.fullAcknowledgement = function (socket, header, ack, stats) {
441  this.lightAcknowledgement(socket, header, ack);
442  say(socket._flowWindowSize, socket._sent[0].length, header, ack, stats);
443}
444
445EndPoint.prototype.lightAcknowledgement = function (socket, header, ack) {
446  var endPoint = this, sent = socket._sent, sequence = sent[0], index;
447  index = binarySearch(bySequence, sequence, ack);
448  if (index != -1 && sent.length == 2) {
449    socket._flowWindowSize -= sent[1].length;
450    sent.length = 1;
451  }
452  if (sent.length == 2) {
453    sequence = sent[1];
454    index = binarySearch(bySequence, sequence, ack);
455  }
456  socket._flowWindowSize -= sequence.splice(0, index).length;
457  endPoint.control(socket, 'header', { type: 0x6, additional: header.additional });
458}
459
460EndPoint.prototype.connect = function (rinfo, header, handshake) {
461  var endPoint = this, server = endPoint.server, timestamp = Math.floor(Date.now() / 6e4);
462
463  // Do not accept new connections if the server is closing.
464  if (server._closing) return;
465
466  handshake = extend(handshake, header);
467
468  if (handshake.connectionType == 1) {
469    handshake.destination = handshake.socketId;
470    handshake.synCookie = synCookie(rinfo, timestamp);
471    endPoint.send('handshake', handshake, rinfo);
472  } else if (handshakeWithValidCookie(handshake, timestamp)) {
473    // Create the socket and initialize it as a listener.
474    var socket = new Socket;
475
476    socket._peer = rinfo;
477    socket._endPoint = endPoint;
478    socket._listener = true;
479    socket._status = 'connected';
480
481    // Increase the count of end point listeners.
482    endPoint.listeners++;
483
484    endPoint.sockets[socket._socketId] = socket;
485
486    handshake.destination = handshake.socketId; 
487    handshake.socketId = socket._socketId; 
488
489    endPoint.send('handshake', handshake, rinfo);
490
491    endPoint.server.emit('connection', socket);
492  }
493
494  function handshakeWithValidCookie (handshake, timestamp) {
495    if (handshake.connectionType != -1) return false;
496    if (synCookie(rinfo, timestamp) == handshake.synCookie) return true;
497    if (synCookie(rinfo, timestamp - 1) == handshake.synCookie) return true;
498    return false; 
499  }
500}
501EndPoint.prototype.transmit = function (socket) {
502  var serializer = common.serializer, dgram = this.dgram,
503      pending = socket._pending, peer = socket._peer, enqueue;
504
505  // If we have data packets to retransmit, they go first, otherwise send a new
506  // data packet.
507  if (false) {
508
509  } else {
510    if (pending.length && !pending[0].length) {
511      pending.shift();
512    }
513
514    if (pending.length) {
515      // TODO: Is pop faster?
516      message = pending[0].shift();
517
518      // Set the sequence number.
519      message.sequence = socket._sequence;
520
521      // We will stash the message and increment the seqeunce number.
522      enqueue = true;
523    }
524  }
525
526  if (message) {
527    serializer.reset();
528    serializer.serialize('header', extend({ control: 0, timestamp: 0 }, message));
529    serializer.write(message.buffer);
530
531    dgram.send(message.buffer, 0, message.buffer.length, peer.port, peer.address);
532  }
533
534  if (enqueue) {
535    socket._flowWindowSize++;
536    // Advance to the socket's next sequence number. The manipulation of the
537    // sent list occurs in both the `Socket` and the `EndPoint`.
538    socket._sequence = socket._sequence + 1 & MAX_SEQ_NO;
539    // When our sequence number wraps, we use a new array of sent packets. This
540    // helps us handle acknowledgements of packets whose squence number is in
541    // the vicinity of a wrap.
542    if (socket._sequence == 0) {
543      socket._sent.unshift([]);
544    }
545    socket._sent[0].push(message);
546  }
547
548  // TODO: Something like this, but after actually calculating the time of the
549  // next packet using the congestion control algorithm.
550  if (pending.length > 1 || pending[0].length) {
551    sendQueue.schedule(socket, 0);
552  }
553}
554
555// Reference counted cache of UDP datagram sockets.
556var endPoints = {};
557
558// Create a new UDP datagram socket from the user specified port and address.
559
560// TODO: IP version.
561function createEndPoint (local) {
562  var endPoint = new EndPoint(local), local = endPoint.local;
563  if (!endPoints[local.port]) endPoints[local.port] = {};
564  return endPoints[local.port][local.address] = endPoint;
565}
566
567// Look up an UDP datagram socket in the cache of bound UDP datagram sockets by
568// the user specified port and address. 
569
570// 
571function lookupEndPoint (local) {
572  // No interfaces bound by the desired port. Note that this would also work for
573  // zero, which indicates an ephemeral binding, but we check for that case
574  // explicitly before calling this function.
575  if (!endPoints[local.port]) return null;
576
577  // Read datagram socket from cache.
578  var endPoint = endPoints[local.port][local.address];
579
580  // If no datagram exists, ensure that we'll be able to create one. This only
581  // inspects ports that have been bound by UDT, not by other protocols, so
582  // there is still an opportunity for error when the UDP bind is invoked.
583  if (!endPoint) {
584    if (endPoints[local.port][0]) {
585      throw new Error('Already bound to all interfaces.');
586    }
587    if (local.address == 0) {
588      throw new Error('Cannot bind to all interfaces because some interfaces are already bound.');
589    }
590  }
591
592  // Return cached datagram socket or nothing.
593  return endPoint;
594}
595
596function validator (ee) {
597  return function (forward) { return check(ee, forward) }
598}
599
600function check (ee, forward) {
601  return function (error) {
602    if (error) {
603      process.nextTick(function () {
604        ee.emit('error', error);
605        ee._destroy();
606      });
607    } else {
608      try {
609        forward.apply(null, __slice.call(arguments, 1));
610      } catch (error) {
611        ee.emit('error', error);
612      }
613    }
614  }
615}
616
617Socket.prototype._nextSequence = function () {
618  var socket = this;
619  if (socket._sequence == MAX_SEQ_NO) {
620    return socket._sequence = 0;
621  } else {
622    return ++socket._sequence;
623  }
624}
625
626Socket.prototype.connect = function (options) {
627  // Convert legacy 'net' module parameters to an options object.
628  if (typeof options != 'object') {
629    var args = net._normalizeConnectArgs(arguments);
630    return Socket.prototype.connect.apply(this, args);
631  }
632
633  var socket = this;
634
635  // TODO: _endPoint.
636  if (socket._dgram) throw new Error('Already connected');
637
638  var peer = { address: options.host, port: options.port };
639  var local = { address: options.localAddress, port: options.localPort };
640
641  if (options.path) throw new Error('UNIX domain sockets are not supported.');
642  if (!options.port) throw new Error('Remote port is required.');
643
644  // Assign reasonable defaults for unspecified connection properties.
645  if (!peer.address) peer.address = '127.0.0.1';
646  if (!local.address) local.address = '0.0.0.0';
647  if (!local.port) local.port = 0;
648
649  if (typeof arguments[1] == 'function') {
650    socket.on('connect', arguments[1]);
651  }
652
653  socket._connecting = true;
654
655  var valid = validator(socket);
656
657  // Resolve DNS now to use the ip as cache key. Lookup handles a interprets
658  // local address as 0.0.0.0.
659  dns.lookup(local.address, valid(localResolved));
660
661  function localResolved (ip, addressType) {
662    local.address = ip;
663
664    // Use an existing datagram socket if one exists.
665    if (local.port == 0) {
666      socket._endPoint = createEndPoint(local);
667    } else if (!(socket._endPoint = lookupEndPoint(local))) {
668      socket._endPoint = createEndPoint(local);
669    }
670
671    dns.lookup(options.address, valid(peerResolved));
672  }
673
674  // Record the DNS resolved IP address.
675  function peerResolved (ip, addressType) {
676    // Possible cancelation during DNS lookup.
677    if (!socket._connecting) return;
678
679    socket._peer = { address: ip || '127.0.0.1', port: options.port };
680
681    // Generate random bytes used to set randomized socket properties.
682    // `crypto.randomBytes` calls OpenSSL `RAND_bytes` to generate the bytes.
683    //
684    //  * [RAND_bytes](http://www.openssl.org/docs/crypto/RAND_bytes.html).
685    //  * [node_crypto.cc](https://github.com/joyent/node/blob/v0.8/src/node_crypto.cc#L4517)
686    crypto.randomBytes(4, valid(randomzied));
687  }
688
689  // Initialize the randomized socket properies.
690  function randomzied (buffer) {
691    // Randomly generated randomness.
692    socket._sequence = buffer.readUInt32BE(0) % MAX_SEQ_NO;
693
694    // The end point sends a packet on our behalf.
695    socket._endPoint.shakeHands(socket);
696  }
697}
698// There is no way to send the UDP packets without copying the user buffer into
699// new buffers. The UDP packets need a header before a chunk of the user data,
700// so we need to write the header, which means we need a buffer we can alter. We
701// cannot borrow the user's buffer.
702// 
703// According to documentation, write returns false if the buffer cannot be
704// written to kernel, if it is queued in user memory, so we can hold onto it for
705// a while if we like. We pushback when the UDT send buffer, as defined by the
706// count of packets, is full. 
707//
708// All this copying and allocation is disheartening. This is a place that needs
709// the attention of some benchmarks. If you can think of a way to avoid the
710// copying, please let me know. Nothing's occurring to me.
711
712
713// Total size of UDT data packet overhead, UDP header plus UDT data header.
714const UDP_HEADER_SIZE = 28;
715const UDT_DATA_HEADER_SIZE = 16;
716
717//
718Socket.prototype.write = function (buffer) {
719  var socket = this,
720      handshake = socket._handshake,
721      size = handshake.maxPacketSize - (UDT_DATA_HEADER_SIZE + UDT_DATA_HEADER_SIZE),
722      packet, count, i, length, message = [];
723
724  count = Math.floor(buffer.length / size);
725  if (buffer.length % size) count++;
726
727  for (i = 0; i < count; i++) {
728    packet = {
729      control: 0,
730      position: 0,
731      inOrder: 1,
732      number: socket._messageNumber,
733      destination: handshake.socketId,
734      buffer: new Buffer(UDT_DATA_HEADER_SIZE + Math.min(buffer.length - i * size, size))
735    };
736    // TODO: Does `Buffer.copy` choose the lesser of source length and
737    // destination length?
738    buffer.copy(packet.buffer, UDT_DATA_HEADER_SIZE, i * size);
739    if (i == 0) packet.position |= 0x2;
740    if (i == count - 1) packet.position |= 0x1;
741    message.push(packet);
742  }
743
744  socket._messageNumber++;
745  if (socket._messageNumber > MAX_MSG_NO) socket._messageNumber = 1;
746
747  socket._pending.push(message);
748
749  sendQueue.schedule(socket, [ 0, 0 ]);
750
751  return true; 
752}
753Socket.prototype.destroy = function () {
754  this._endPoint.shutdown(this);
755}
756Socket.prototype._destroy = Socket.prototype.destroy;
757
758function Server () {
759  if (!(this instanceof Server)) return new Server(arguments[0], arguments[1]);
760
761  events.EventEmitter.call(this);
762
763  var server = this;
764
765  var options;
766
767  if (typeof arguments[0] == 'function') {
768    options = {};
769    server.on('connection', arguments[0]);
770  } else {
771    options = arguments[0] || {};
772    if (typeof arguments[1] == 'function') {
773      server.on('connection', arguments[1]);
774    }
775  }
776
777  // The Node.js `net` module uses a property for connections because the
778  // connections property is disabled if the server is running in a
779  // multi-process model, if it has "slaves." UDT does not support multi-process
780  // model, so connections is plain-old property.
781
782  //
783  this.connections = 0;
784}
785util.inherits(Server, events.EventEmitter);
786exports.Server = Server;
787
788// TODO: Consolidate.
789function selectEndPoint (local) {
790  var endPoint;
791  // Use an existing datagram socket if one exists.
792  if (local.port == 0) {
793    endPoint = createEndPoint(local);
794  } else if (!(endPoint = lookupEndPoint(local))) {
795    endPoint = createEndPoint(local);
796  }
797  return endPoint;
798}
799
800Server.prototype.listen = function () {
801  var server = this;
802
803  var lastArg = arguments[arguments.length - 1];
804  if (typeof lastArg == 'function') {
805    server.once('listening', lastArg);
806  }
807
808  var valid = validator(server);
809
810  var options = { port: arguments[0] || 0 };
811  dns.lookup(arguments[1], valid(resolved));
812
813  function resolved (ip, addressType) {
814    options.address = ip || '0.0.0.0';
815
816    var endPoint = server._endPoint = selectEndPoint(options);
817
818    if (endPoint.server) {
819      throw new Error('already bound to UDT server');
820    }
821
822    endPoint.server = server;
823    console.log(endPoint.local);
824
825    process.nextTick(function () {
826      server.emit('listening');
827    });
828  }
829}
830
831Server.prototype.close = function (callback) {
832  var server = this, endPoint = server._endPoint;
833
834  if (callback) server.once('close', callback);
835
836  server._closing = true;
837
838  if (endPoint.listeners == 0) {
839    endPoint._server = null; 
840    server.emit('close');
841  }
842}
843
844const SYN_COOKIE_SALT = crypto.randomBytes(64).toString('binary');
845function synCookie (address, timestamp) {
846  var hash = crypto.createHash('sha1');
847  hash.update(SYN_COOKIE_SALT + ':' + address.host + ':' + address.port + ':' + timestamp);
848  return parseInt(hash.digest('hex').substring(0, 8), 16);
849}
850
851exports.createServer = function () {
852  return new Server(arguments[0], arguments[1]);
853}
854
855function toArray (buffer) {
856  return buffer.toString('hex').replace(/(..)/g, ':$1')
857                               .replace(/(.{12})/g, '\n$1')
858                               .replace(/\n:/g, '\n');
859}