在 NodeJS 协议中实现 STARTTLS

2023-12-08

我正在尝试将 STARTTLS 升级添加到现有协议(当前以纯文本形式运行)。

首先,我使用一个简单的基于行的回显服务器(这是一个可怕的组装,没有错误处理或将数据包处理成行 - 但它通常只是在控制台一次发送一行到标准输入时工作)。

我认为我的服务器是正确的,但是当我输入时两端都会出现相同的错误starttls:

events.js:72
        throw er; // Unhandled 'error' event
              ^
Error: 139652888721216:error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol:../deps/openssl/openssl/ssl/s23_clnt.c:766:

    at SlabBuffer.use (tls.js:232:18)
    at CleartextStream.read [as _read] (tls.js:450:29)
    at CleartextStream.Readable.read (_stream_readable.js:320:10)
    at EncryptedStream.write [as _write] (tls.js:366:25)
    at doWrite (_stream_writable.js:221:10)
    at writeOrBuffer (_stream_writable.js:211:5)
    at EncryptedStream.Writable.write (_stream_writable.js:180:11)
    at Socket.ondata (stream.js:51:26)
    at Socket.EventEmitter.emit (events.js:95:17)
    at Socket.<anonymous> (_stream_readable.js:746:14)

我是否完全误解了如何在客户端进行升级?

目前,我正在使用相同的方法将 TLS 添加到两端的普通流。这感觉是错误的,因为客户端和服务器都将尝试在协商中扮演相同的角色。

tlsserver.js:

r tls = require('tls');
var net = require('net');
var fs = require('fs');

var options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),

  // This is necessary only if using the client certificate authentication.
  requestCert: true,

  // This is necessary only if the client uses the self-signed certificate.
  ca: [ fs.readFileSync('client-cert.pem') ],

  rejectUnauthorized: false
};

var server = net.createServer(function(socket) {
  socket.setEncoding('utf8');
  socket.on('data', function(data) {
    console.log('plain data: ', data);
    // FIXME: this is not robust, it should be processing the stream into lines
    if (data.substr(0, 8) === 'starttls') {
      console.log('server starting TLS');
      //socket.write('server starting TLS');
      socket.removeAllListeners('data');

      options.socket = socket;
      sec_socket = tls.connect(options, (function() {
        sec_socket.on('data', function() {
          console.log('secure data: ', data);
        });
        return callback(null, true);
      }).bind(this));
    } else {
      console.log('plain data', data);
    }
  });
});

server.listen(9999, function() {
  console.log('server bound');
});

客户端.js:

var tls = require('tls');
var fs = require('fs');
var net = require('net');

var options = {
  // These are necessary only if using the client certificate authentication
  key: fs.readFileSync('client-key.pem'),
  cert: fs.readFileSync('client-cert.pem'),

  // This is necessary only if the server uses the self-signed certificate
  ca: [ fs.readFileSync('server-cert.pem') ],
  rejectUnauthorized: false
};


var socket = new net.Socket();
var sec_socket = undefined;

socket.setEncoding('utf8');
socket.on('data', function(data) {
  console.log('plain data:', data);
});
socket.connect(9999, function() {
  process.stdin.setEncoding('utf8');
  process.stdin.on('data', function(data) {
    if (!sec_socket) {
      console.log('sending plain:', data);
      socket.write(data);
    } else {
      console.log('sending secure:', data);
      sec_socket.write(data);
    }
    if (data.substr(0, 8) === 'starttls') {
      console.log('client starting tls');
      socket.removeAllListeners('data');
      options.socket = socket;
      sec_socket = tls.connect(options, (function() {
        sec_socket.on('data', function() {
          console.log('secure data: ', data);
        });
        return callback(null, true);
      }).bind(this));
    }
  });
});

感谢马特中士的回答,一切顺利。我的代码现在看起来像:

服务器.js:

var ts = require('./tls_socket');
var fs = require('fs');

var options = {
  key: fs.readFileSync('server-key.pem'),
  cert: fs.readFileSync('server-cert.pem'),

  // This is necessary only if using the client certificate authentication.
  requestCert: false,

  // This is necessary only if the client uses the self-signed certificate.
  ca: [ fs.readFileSync('client-cert.pem') ],

  rejectUnauthorized: false
};



var server = ts.createServer(function(socket) {
  console.log('connected');
  socket.on('data', function(data) {
    console.log('data', data);
    if (data.length === 9) {
      console.log('upgrading to TLS');
      socket.upgrade(options, function() {
        console.log('upgraded to TLS');
      });
    }
  });
});
server.listen(9999);

客户端.js:

var ts = require('./tls_socket');
var fs = require('fs');
var crypto = require('crypto');

var options = {
  // These are necessary only if using the client certificate authentication
  key: fs.readFileSync('client-key.pem'),
  cert: fs.readFileSync('client-cert.pem'),

  // This is necessary only if the server uses the self-signed certificate
  ca: [ fs.readFileSync('server-cert.pem') ],

  rejectUnauthorized: false
};

var socket = ts.connect(9999, 'localhost', function() {
  console.log('secured');
});

process.stdin.on('data', function(data) {
  console.log('sending:', data);
  socket.write(data);
  if (data.length === 9) {
    socket.upgrade(options);
  }
});

tls_socket.js:

"use strict";
/*----------------------------------------------------------------------------------------------*/
/* Obtained and modified from http://js.5sh.net/starttls.js on 8/18/2011.                       */
/*----------------------------------------------------------------------------------------------*/

var tls = require('tls');
var crypto = require('crypto');
var util = require('util');
var net = require('net');
var stream = require('stream');
var SSL_OP_ALL = require('constants').SSL_OP_ALL;

// provides a common socket for attaching
// and detaching from either main socket, or crypto socket
function pluggableStream(socket) {
    stream.Stream.call(this);
    this.readable = this.writable = true;
    this._timeout = 0;
    this._keepalive = false;
    this._writeState = true;
    this._pending = [];
    this._pendingCallbacks = [];
    if (socket)
        this.attach(socket);
}

util.inherits(pluggableStream, stream.Stream);

pluggableStream.prototype.pause = function () {
    if (this.targetsocket.pause) {
        this.targetsocket.pause();
        this.readable = false;
    }
}

pluggableStream.prototype.resume = function () {
    if (this.targetsocket.resume) {
        this.readable = true;
        this.targetsocket.resume();
    }
}

pluggableStream.prototype.attach = function (socket) {
    var self = this;
    self.targetsocket = socket;
    self.targetsocket.on('data', function (data) {
        self.emit('data', data);
    });
    self.targetsocket.on('connect', function (a, b) {
        self.emit('connect', a, b);
    });
    self.targetsocket.on('secureConnection', function (a, b) {
        self.emit('secureConnection', a, b);
        self.emit('secure', a, b);
    });
    self.targetsocket.on('secure', function (a, b) {
        self.emit('secureConnection', a, b);
        self.emit('secure', a, b);
    });
    self.targetsocket.on('end', function () {
        self.writable = self.targetsocket.writable;
        self.emit('end');
    });
    self.targetsocket.on('close', function (had_error) {
        self.writable = self.targetsocket.writable;
        self.emit('close', had_error);
    });
    self.targetsocket.on('drain', function () {
        self.emit('drain');
    });
    self.targetsocket.on('error', function (exception) {
        self.writable = self.targetsocket.writable;
        self.emit('error', exception);
    });
    self.targetsocket.on('timeout', function () {
        self.emit('timeout');
    });
    if (self.targetsocket.remotePort) {
        self.remotePort = self.targetsocket.remotePort;
    }
    if (self.targetsocket.remoteAddress) {
        self.remoteAddress = self.targetsocket.remoteAddress;
    }
};

pluggableStream.prototype.clean = function (data) {
    if (this.targetsocket && this.targetsocket.removeAllListeners) {
        this.targetsocket.removeAllListeners('data');
        this.targetsocket.removeAllListeners('secureConnection');
        this.targetsocket.removeAllListeners('secure');
        this.targetsocket.removeAllListeners('end');
        this.targetsocket.removeAllListeners('close');
        this.targetsocket.removeAllListeners('error');
        this.targetsocket.removeAllListeners('drain');
    }
    this.targetsocket = {};
    this.targetsocket.write = function () {};
};

pluggableStream.prototype.write = function (data, encoding, callback) {
    if (this.targetsocket.write) {
        return this.targetsocket.write(data, encoding, callback);
    }
    return false;
};

pluggableStream.prototype.end = function (data, encoding) {
    if (this.targetsocket.end) {
        return this.targetsocket.end(data, encoding);
    }
}

pluggableStream.prototype.destroySoon = function () {
    if (this.targetsocket.destroySoon) {
        return this.targetsocket.destroySoon();
    }
}

pluggableStream.prototype.destroy = function () {
    if (this.targetsocket.destroy) {
        return this.targetsocket.destroy();
    }
}

pluggableStream.prototype.setKeepAlive = function (bool) {
    this._keepalive = bool;
    return this.targetsocket.setKeepAlive(bool);
};

pluggableStream.prototype.setNoDelay = function (/* true||false */) {
};

pluggableStream.prototype.setTimeout = function (timeout) {
    this._timeout = timeout;
    return this.targetsocket.setTimeout(timeout);
};

function pipe(pair, socket) {
    pair.encrypted.pipe(socket);
    socket.pipe(pair.encrypted);

    pair.fd = socket.fd;
    var cleartext = pair.cleartext;
    cleartext.socket = socket;
    cleartext.encrypted = pair.encrypted;
    cleartext.authorized = false;

    function onerror(e) {
        if (cleartext._controlReleased) {
            cleartext.emit('error', e);
        }
    }

    function onclose() {
        socket.removeListener('error', onerror);
        socket.removeListener('close', onclose);
    }

    socket.on('error', onerror);
    socket.on('close', onclose);

    return cleartext;
}

function createServer(cb) {
    var serv = net.createServer(function (cryptoSocket) {

        var socket = new pluggableStream(cryptoSocket);

        socket.upgrade = function (options, cb) {
            console.log("Upgrading to TLS");

            socket.clean();
            cryptoSocket.removeAllListeners('data');

            // Set SSL_OP_ALL for maximum compatibility with broken clients
            // See http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
            if (!options) options = {};
            // TODO: bug in Node means we can't do this until it's fixed
            // options.secureOptions = SSL_OP_ALL;

            var sslcontext = crypto.createCredentials(options);

            var pair = tls.createSecurePair(sslcontext, true, true, false);

            var cleartext = pipe(pair, cryptoSocket);

            pair.on('error', function(exception) {
                socket.emit('error', exception);
            });

            pair.on('secure', function() {
                var verifyError = (pair.ssl || pair._ssl).verifyError();

                console.log("TLS secured.");
                if (verifyError) {
                    cleartext.authorized = false;
                    cleartext.authorizationError = verifyError;
                } else {
                    cleartext.authorized = true;
                }
                var cert = pair.cleartext.getPeerCertificate();
                if (pair.cleartext.getCipher) {
                    var cipher = pair.cleartext.getCipher();
                }
                socket.emit('secure');
                if (cb) cb(cleartext.authorized, verifyError, cert, cipher);
            });

            cleartext._controlReleased = true;

            socket.cleartext = cleartext;

            if (socket._timeout) {
                cleartext.setTimeout(socket._timeout);
            }

            cleartext.setKeepAlive(socket._keepalive);

            socket.attach(socket.cleartext);
        };

        cb(socket);
    });

    return serv;
}

if (require('semver').gt(process.version, '0.7.0')) {
    var _net_connect = function (options) {
        return net.connect(options);
    }
}
else {
    var _net_connect = function (options) {
        return net.connect(options.port, options.host);
    }
}

function connect(port, host, cb) {
    var options = {};
    if (typeof port === 'object') {
        options = port;
        cb = host;
    }
    else {
        options.port = port;
        options.host = host;
    }

    var cryptoSocket = _net_connect(options);

    var socket = new pluggableStream(cryptoSocket);

    socket.upgrade = function (options) {
        socket.clean();
        cryptoSocket.removeAllListeners('data');

        // Set SSL_OP_ALL for maximum compatibility with broken servers
        // See http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html
        if (!options) options = {};
        // TODO: bug in Node means we can't do this until it's fixed
        // options.secureOptions = SSL_OP_ALL;

        var sslcontext = crypto.createCredentials(options);

        var pair = tls.createSecurePair(sslcontext, false);

        socket.pair = pair;

        var cleartext = pipe(pair, cryptoSocket);

        pair.on('error', function(exception) {
            socket.emit('error', exception);
        });

        pair.on('secure', function() {
            var verifyError = (pair.ssl || pair._ssl).verifyError();

            console.log("client TLS secured.");
            if (verifyError) {
                cleartext.authorized = false;
                cleartext.authorizationError = verifyError;
            } else {
                cleartext.authorized = true;
            }

            if (cb) cb();

            socket.emit('secure');
        });

        cleartext._controlReleased = true;
        socket.cleartext = cleartext;

        if (socket._timeout) {
            cleartext.setTimeout(socket._timeout);
        }

        cleartext.setKeepAlive(socket._keepalive);

        socket.attach(socket.cleartext);

        console.log("client TLS upgrade in progress, awaiting secured.");
    };

    return (socket);
}

exports.connect = connect;
exports.createConnection = connect;
exports.Server = createServer;
exports.createServer = createServer;

不幸的是,tls.connect() 不支持服务器进行升级。

您必须使用与 Haraka 的做法类似的代码 - 基本上是使用 SecurePair 创建您自己的垫片。

请参阅此处了解我们使用的代码:https://github.com/baudehlo/Haraka/blob/master/tls_socket.js#L171

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 NodeJS 协议中实现 STARTTLS 的相关文章

  • 将输入数据发送到node.js中的子进程

    我正在编写代码以在 Node js 环境中创建在线 C 编译器 使用spawn函数我创建了一个子进程 它将编译代码并执行它并将输出发送回用户 但我需要将输入发送到正在运行的 C 程序 我用了child stdin write data 用于
  • 在 Node.js 中以事务方式写入文件

    我有一个 Node js 应用程序 它将一些配置数据存储在文件中 如果更改某些设置 配置文件将写入磁盘 目前 我正在使用一个简单的fs writeFile 现在我的问题是 当 Node js 在写入文件时崩溃时会发生什么 磁盘上是否有可能存
  • 装饰器中间件模式的 Typescript 类型

    我正在考虑节点中间件 在各种框架中 通常 中间件会向请求或响应对象添加一个属性 然后该属性可供在其之后注册的任何中间件使用 此模型的一个问题是您无法进行有效的打字 为了进行演示 这里有一个此类框架的高度简化的模型 在这种情况下 一切都是同步
  • AWS Lambda - Nodejs 函数不会返回数据

    我是 NodeJS 函数调用的新手 我已经在屏幕上敲了几个小时了 我所有的谷歌搜索都没有帮助 所以我有一个 AWS Lambda 函数 它接收一个带有单个 ID 号的 JSON 对象 该 ID 号将被传递并最终作为 myid 发送到 get
  • 节点遗留 url.parse 已弃用,用什么代替?

    require url parse someurl com page 已被仅弃用 并且我们严格的 linter 对此不满意 我尝试用互联网建议的内容替换我们的代码中的它new URL someurl com page 在大多数情况下都有效
  • 从 puppeteer PDF 中删除分页符?

    我目前正在尝试查看是否有一种方法可以删除我的 puppeteer PDF 中的分页符 因为我当前的 PDF 设置中的一些分页符正在以一种奇怪的方式切断文本 我正在谈论的内容的屏幕截图 我的傀儡代码 app get companyId pdf
  • 使用nodejs的sequelize更新多对多连接表

    我有一个产品表和一个类别表 一个产品可以有多个类别 一个类别可以有多个产品 因此我有一个 ProductsCategories 表来处理多对多连接 在下面的示例中 我尝试将我的一款产品 ID 为 1 与 3 个不同的类别 ID 为 1 2
  • 在 Windows 上使用 IPython 笔记本时出现 500 服务器错误

    我刚刚在 Windows 7 Professional 64 位上全新安装了 IPython 笔记本 我采取的步骤是 从以下位置安装 Python 3 4 1http python org http python org gt pip in
  • Socket.io、集群、快速和同步事件

    1周以来我遇到了一个大问题 我尝试将实际在单核上运行的 node JS 项目转换为具有集群的多核 对于 websockets 目前 我对事件没有任何问题 但是 对于 xhr polling 或 jsonp polling 我在集群模式下使用
  • 如何使用 Django 了解服务器是否安全(使用 https)

    我正在开发一个基于 Django 的应用程序 我想知道是否有办法知道我的服务器使用的是 http 连接还是 https 我知道使用 import socket if socket gethostname startswith 我可以获取主机
  • 使用 Javascript 展平父子 JSON 数组

    我有一个如下所示的数组 const arr name x type type1 parent name a type type1 name y type type1 parent name b type type1 我想将其展平并最终得到如
  • 使用 php、gmail 和 swiftmailer 发送电子邮件导致与 SSL 相关的错误

    这是我的 PHP 代码 function SendCookieToTheMail require once swift mailer lib swift required php Create the Transport transport
  • 设置环境变量供节点检索

    我正在尝试遵循教程 它说 有几种加载凭据的方法 从环境变量加载 从磁盘上的 JSON 文件加载 键需要如下所示 USER ID USER KEY 这意味着如果您正确设置环境变量 您 根本不需要管理应用程序中的凭据 根据一些谷歌搜索 我似乎需
  • 缩短node.js和mongoose中的ObjectId

    我的网址目前如下所示 http www sitename com watch companyId 507f1f77bcf86cd799439011 employeeId 507f191e810c19729de860ea someOtherI
  • Webpack 如何构建生产代码以及如何使用它

    我对 webpack 很陌生 我发现在生产构建中我们可以减少整体代码的大小 目前 webpack 构建的文件大小约为 8MB main js 的文件大小约为 5MB 如何减少生产构建中的代码大小 我从互联网上找到了一个示例 webpack
  • 如何在节点soap中查看请求和响应数据?

    我正在尝试使用肥皂API节点肥皂 https github com vpulim node soap 我的响应无法解析 我想知道如何查看控制台的请求和响应数据以简化错误查找过程 由于 Node Soap 使用请求库 因此可以通过以下方式对其
  • openssl ciphers 命令将密码显示为 SSL 而不是 TLS

    我的应用程序出现 SSL 握手错误的问题 开发人员认为这是因为我们的服务器上的密码错误 他们尝试使用名为 TLS RSA WITH AES 256 CBC SHA 的密码 据我所知 该密码的 openssl 名称是 AES256 SHA 当
  • 跳过测试文件 Jest 中的一项测试

    我正在使用 Jest 框架并有一个测试套件 我想关闭 跳过其中一项测试 谷歌搜索文档没有给我答案 您知道答案或需要检查的信息来源吗 我在这里找到了答案 https devhints io jest https devhints io jes
  • 获取与请求

    我正在使用 JSON 流并尝试使用 fetch 来使用它 该流每隔几秒发出一些数据 仅当流关闭服务器端时 使用 fetch 来使用流才可以访问数据 例如 var target the url var options method POST
  • 无法从亚马逊Windows实例的公共IP访问node.js服务器

    我正在 Windows 服务器的亚马逊微实例上运行 Node js 服务器 我无法使用亚马逊实例的公共IP访问node js服务器 我可以使用 localhost 12345 从实例访问 node js 服务器 但无法使用实例的公共 IP

随机推荐

  • WPF XAML StringFormat:C# 4.0 中的文化解决方法被破坏?

    周围的工作 FrameworkElement LanguageProperty OverrideMetadata typeof FrameworkElement new FrameworkPropertyMetadata XmlLangua
  • 使用 Jersey 2 (HK2) DI 注册 Dropwizard 配置

    在我的 Dropwizard 1 2 4 应用程序中 我无法将 Dropwizard 配置注入到由 HK2 实例化的类中 实现这一目标的最佳方法是什么 只需绑定配置即可instance Override public void run fi
  • 我可以信任 Java SecurityManager 沙箱吗?

    我正在编写一个 JavaFX2 应用程序 它接受从远程位置加载的任意代码 对我来说 使用自定义 SecurityManager ClassLoader 和 ProtectionDomain 是最佳选择 不幸的是 这似乎与用于沙箱小程序的设置
  • 手动指定特定链接符号的重新映射

    在不修改这两个源文件的情况下 有没有办法获取编译它们生成的目标文件 并说服链接器链接foo在 main v1 c 中bar在bar c main v1 c void foo void int main void foo bar c incl
  • JavaScript 是多线程的吗?

    这是我的问题 我需要使用 jQuery getScript 动态下载多个脚本并执行某些JavaScript加载所有脚本后的代码 所以我的计划是做这样的事情 function GetScripts scripts callback var l
  • Python 和 C++ 模数

    我正在学习 C 在尝试编写一个小程序时 我发现了一些奇怪的东西 这是关于模数 C 代码 cout lt lt 325 325 100 lt lt endl 300 cout lt lt 325 325 100 300 和Python代码 p
  • 关于泛型方法调用的 Java 类型提示

    我想知道调用具有如下签名的静态方法的正确方法是什么 public static
  • 如何获取单选按钮的文本(而不是值)

    我知道我可以获取单选按钮的 值 属性 但我发现获取单选按钮的文本非常困难 考虑下面的例子 它有 3 个单选按钮 并尝试提醒第一个单选按钮的值 红色 然后尝试提醒单选按钮的文本 苹果 但失败了 获取几乎任何元素的文本都可以使用 elem ch
  • 正则表达式在文件夹中查找文件

    如何查找文件夹中与正则表达式模式匹配的所有文件 Thanks The GetFiles方法允许您指定通配符模式 但不是真正的正则表达式 另一种可能性是简单地循环遍历文件并根据正则表达式验证它们的名称 IEnumerable
  • 将图像大小调整到给定边界区域的最简单方法是什么?

    我想创建一个函数 例如 def generateThumbnail self width height Generates thumbnails for an image im Image open self file im thumbna
  • MYSQLi 错误:用户已经拥有超过“max_user_connections”的活动连接[重复]

    这个问题在这里已经有答案了 我正在运行的网站上出现以下错误 我不明白为什么会这样 因为它在我的本地主机上运行良好 跟楼主有关系吗 我在 Unix 服务器上 Warning mysqli mysqli mysqli mysqli 42000
  • NodeJS 将 Int16Array 二进制缓冲区转换为 Google Speech API 的 LINEAR16 编码原始流

    我正在尝试在节点服务器中将语音转换为文本 其中使用 AudioContext 在浏览器中进行语音录制 我能够通过binaryType arraybuffer的WebSocket连接将int16Array缓冲区 记录的数据 发送到我的节点服务
  • 从列号获取 Excel 样式的列名称

    这是在提供行和列 ID 时提供列名称的 代码 但当我给出如下值时row 1 and col 104 它应该返回CZ 但它返回D row 1 col 104 div col column label str while div div mod
  • 如何从左到右移动uiview,反之亦然

    您好 我正在开发一个应用程序 我为一个视图制作了从左到右 从右到左移动的动画 并更改该视图中包含的标签的值 但是当我单击左或右按钮时 该视图将被删除新视图覆盖旧视图 所以我不想覆盖 只是我想添加新视图 我的代码是 void centerAn
  • 文本文件的行数

    我正在尝试创建一个函数 它接受文件名 即 data txt 并生成该文件的行数 data txt 24 42 45 54 67 76 89 98 12 21 99 99 33 33 下面的代码是我尝试构建一个函数 该函数接受文件名 data
  • mongoose 和 mongoJS 有什么区别?我应该使用哪个?

    我只是想知道 mongoose 和 mongoJS 有什么区别 那么 如果我们使用 mongoose 或 mongoJS 有何优点和缺点 因为我们知道连接 NodeJS 和 MongoDB 有很多依赖关系 例如猫鼬和 mongoJS Mon
  • 如何从 HTML 文件中提取元标签并在 SOLR 和 TIKA 中对其进行索引

    我正在尝试提取 HTML 文件的元标签 并通过 tika 集成将它们索引到 solr 中 我无法使用 Tika 提取这些元标记 也无法在 solr 中显示 我的 HTML 文件看起来像这样
  • 嵌套对象的默认值

    假设我有以下提供默认值的对象 default values a 0 b 0 c aa 0 bb 0 我还有另一个对象可以覆盖其中一些默认值 override values a 5 c aa 5 我想要的是将这两个对象结合起来 从而产生 co
  • 使用 C# 通过 Skype 进行通话

    我想开发一个盲人辅助软件 比如Jarvis 它是一个 C 表单应用程序 我需要将我的 C 项目与 Skype API 连接起来 以便使用 Skype 调用某个用户名 当我使用命令时Call John 它通过 Skype 呼叫 John 进行
  • 在 NodeJS 协议中实现 STARTTLS

    我正在尝试将 STARTTLS 升级添加到现有协议 当前以纯文本形式运行 首先 我使用一个简单的基于行的回显服务器 这是一个可怕的组装 没有错误处理或将数据包处理成行 但它通常只是在控制台一次发送一行到标准输入时工作 我认为我的服务器是正确