IceAgent.prototype.bindingSuccess = function (socket, packet, rinfo) { var candidatePair = this.checklist.pendingRequestTransactions[packet.transactionID]; delete this.checklist.pendingRequestTransactions[packet.transactionID]; if (!candidatePair) { // This usually occurs once for the external STUN server we first spoke to, // ie. stun.l.google.com console.warn('binding success for unknown transactionID'); return; } var dest = Packet.getAttribute(packet).xorMappedAddress; if (net.isIPv6(dest.host)) { dest.host = normalizeIPv6(dest.host); } // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.1.3.2 if (matchingSourceDest(rinfo, dest, candidatePair)) { // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.1.3.2.3 candidatePair.succeed(); this.checklist.unfreezeAll(); } else { // TODO: Issue #52 // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.1.3.2.1 // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.2.1.3 console.warn('Found a peer reflexive candidate'); debug.printPeerReflexive(rinfo, dest, candidatePair); } // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.1.3.2.2 // TODO: there's more to do here candidatePair.valid = true; this.checklist.validList.push(candidatePair); // https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-6.1.3.3 this.checklist.checkForFailure(); };
SMTPConnection.prototype.handler_XFORWARD = function (command, callback) { // check if user is authorized to perform this command if (!this._server.options.useXForward) { this.send(550, 'Error: Not allowed'); return callback(); } // not allowed to change properties if already processing mail if (this.session.envelope.mailFrom) { this.send(503, 'Error: Mail transaction in progress'); return callback(); } var allowedKeys = ['NAME', 'ADDR', 'PORT', 'PROTO', 'HELO', 'IDENT', 'SOURCE']; var parts = command.toString().trim().split(/\s+/); var key, value; var data = new Map(); parts.shift(); // remove XFORWARD prefix if (!parts.length) { this.send(501, 'Error: Bad command parameter syntax'); return callback(); } // parse and validate arguments for (var i = 0, len = parts.length; i < len; i++) { value = parts[i].split('='); key = value.shift(); if (value.length !== 1 || allowedKeys.indexOf(key.toUpperCase()) < 0) { this.send(501, 'Error: Bad command parameter syntax'); return callback(); } key = key.toUpperCase(); if (data.has(key)) { // ignore duplicate keys continue; } // value is xtext value = (value[0] || '').replace(/\+([0-9A-F]{2})/g, function (match, hex) { return unescape('%' + hex); }); if (value.toUpperCase() === '[UNAVAILABLE]') { value = false; } data.set(key, value); switch (key) { case 'ADDR': if (value) { value = value.replace(/^IPV6:/i, ''); // IPv6 addresses are prefixed with "IPv6:" if (!net.isIP(value)) { this.send(501, 'Error: Bad command parameter syntax. Invalid address'); return callback(); } if (net.isIPv6(value)) { value = ipv6normalize(value); } this._server.logger.info('[%s] XFORWARD from %s through %s', this._id, value, this.remoteAddress); } break; case 'NAME': value = value || ''; this._server.logger.info('[%s] XFORWARD hostname resolved as "%s"', this._id, value); break; default: // other values are not relevant } this._xForward.set(key, value); } // success this.send(250, 'OK'); callback(); };
SMTPConnection.prototype.handler_XCLIENT = function (command, callback) { // check if user is authorized to perform this command if (this._xClient.has('ADDR') || !this._server.options.useXClient) { this.send(550, 'Error: Not allowed'); return callback(); } // not allowed to change properties if already processing mail if (this.session.envelope.mailFrom) { this.send(503, 'Error: Mail transaction in progress'); return callback(); } var allowedKeys = ['NAME', 'ADDR', 'PORT', 'PROTO', 'HELO', 'LOGIN']; var parts = command.toString().trim().split(/\s+/); var key, value; var data = new Map(); parts.shift(); // remove XCLIENT prefix if (!parts.length) { this.send(501, 'Error: Bad command parameter syntax'); return callback(); } // parse and validate arguments for (var i = 0, len = parts.length; i < len; i++) { value = parts[i].split('='); key = value.shift(); if (value.length !== 1 || allowedKeys.indexOf(key.toUpperCase()) < 0) { this.send(501, 'Error: Bad command parameter syntax'); return callback(); } key = key.toUpperCase(); // value is xtext value = (value[0] || '').replace(/\+([0-9A-F]{2})/g, function (match, hex) { return unescape('%' + hex); }); if (['[UNAVAILABLE]', '[TEMPUNAVAIL]'].indexOf(value.toUpperCase()) >= 0) { value = false; } if (data.has(key)) { // ignore duplicate keys continue; } data.set(key, value); switch (key) { case 'LOGIN': if (!value) { if (this.session.user) { this._server.logger.info('[%s] User deauthenticated using %s', this._id, 'XCLIENT'); } } else { this._server.logger.info('[%s] %s authenticated using %s', this._id, value, 'XCLIENT'); this.session.user = { username: value }; } break; case 'ADDR': if (value) { value = value.replace(/^IPV6:/i, ''); // IPv6 addresses are prefixed with "IPv6:" if (!net.isIP(value)) { this.send(501, 'Error: Bad command parameter syntax. Invalid address'); return callback(); } if (net.isIPv6(value)) { value = ipv6normalize(value); } this._server.logger.info('[%s] XCLIENT from %s through %s', this._id, value, this.remoteAddress); // store original value for reference as ADDR:DEFAULT if (!this._xClient.has('ADDR:DEFAULT')) { this._xClient.set('ADDR:DEFAULT', this.remoteAddress || ''); } this.remoteAddress = value; this.hostNameAppearsAs = false; // reset client provided hostname, require HELO/EHLO } break; case 'NAME': value = value || ''; this._server.logger.info('[%s] XCLIENT hostname resolved as "%s"', this._id, value); // store original value for reference as NAME:DEFAULT if (!this._xClient.has('NAME:DEFAULT')) { this._xClient.set('NAME:DEFAULT', this.clientHostname || ''); } this.clientHostname = value.toLowerCase(); break; default: // other values are not relevant } this._xClient.set(key, value); } // Use [ADDR] if NAME was empty if (this.remoteAddress && !this.clientHostname) { this.clientHostname = '[' + this.remoteAddress + ']'; } // success this.send(220, this.name + ' ESMTP' + (this._server.options.banner ? ' ' + this._server.options.banner : '')); callback(); };
/** * Creates a handler for new socket * * @constructor * @param {Object} server Server instance * @param {Object} socket Socket instance */ function SMTPConnection(server, socket) { EventEmitter.call(this); // Random session ID, used for logging this._id = crypto.randomBytes(9).toString('base64'); this._server = server; this._socket = socket; // session data (envelope, user etc.) this.session = this.session = { id: this._id }; // how many messages have been processed this._transactionCounter = 0; // Do not allow input from client until initial greeting has been sent this._ready = false; // If true then the connection is currently being upgraded to TLS this._upgrading = false; // Set handler for incoming command and handler bypass detection by command name this._nextHandler = false; // Parser instance for the incoming stream this._parser = new SMTPStream(); // Set handler for incoming commands this._parser.oncommand = this._onCommand.bind(this); // if currently in data mode, this stream gets the content of incoming message this._dataStream = false; // If true, then the connection is using TLS this.secure = !!this._server.options.secure; // Store remote address for later usage this.remoteAddress = this._socket.remoteAddress; // normalize IPv6 addresses if (this.remoteAddress && net.isIPv6(this.remoteAddress)) { this.remoteAddress = ipv6normalize(this.remoteAddress); } // Error counter - if too many commands in non-authenticated state are used, then disconnect this._unauthenticatedCommands = 0; // Error counter - if too many invalid commands are used, then disconnect this._unrecognizedCommands = 0; // Server hostname for the greegins this.name = this._server.options.name || os.hostname(); // Resolved hostname for remote IP address this.clientHostname = false; // The hostname client identifies itself with this.hostNameAppearsAs = false; // data passed from XCLIENT command this._xClient = new Map(); // data passed from XFORWARD command this._xForward = new Map(); // increment connection count this._closing = false; this._closed = false; }