mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
TCP cleanup, added basis of web interface
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
var servertalk = require('./servertalk_client.js');
|
||||
var fs = require('fs');
|
||||
var settings = JSON.parse(fs.readFileSync('settings.json', 'utf8'));
|
||||
|
||||
var client = new servertalk.client();
|
||||
|
||||
client.Init(settings.addr, settings.port, false, 'WebInterface', settings.key);
|
||||
|
||||
client.on('connecting', function(){
|
||||
console.log('Connecting...');
|
||||
});
|
||||
|
||||
client.on('connect', function(){
|
||||
console.log('Connected...');
|
||||
|
||||
this.Send(47, Buffer.from(JSON.stringify({ method: 'IsLocked', params: [], id: '12345' })));
|
||||
this.Send(47, Buffer.from(JSON.stringify({ method: 'Lock', params: [] })));
|
||||
this.Send(47, Buffer.from(JSON.stringify({ method: 'IsLocked', params: [], id: '12346' })));
|
||||
this.Send(47, Buffer.from(JSON.stringify({ method: 'Unlock', params: [] })));
|
||||
this.Send(47, Buffer.from(JSON.stringify({ method: 'IsLocked', params: [], id: '12347' })));
|
||||
});
|
||||
|
||||
client.on('close', function(){
|
||||
console.log('Closed');
|
||||
});
|
||||
|
||||
client.on('error', function(err){
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
client.on('message', function(opcode, packet) {
|
||||
console.log(Buffer.from(packet).toString('utf8'));
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "wi",
|
||||
"version": "1.0.0",
|
||||
"description": "Web interface connection for EQEmu",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "KimLS",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"libsodium": "^0.4.8",
|
||||
"libsodium-wrappers": "^0.4.8"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
var net = require('net');
|
||||
var sodium = require('libsodium-wrappers');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
var ServertalkPacketType =
|
||||
{
|
||||
ServertalkClientHello: 1,
|
||||
ServertalkServerHello: 2,
|
||||
ServertalkClientHandshake: 3,
|
||||
ServertalkClientDowngradeSecurityHandshake: 4,
|
||||
ServertalkMessage: 5,
|
||||
};
|
||||
|
||||
class ServertalkClient extends EventEmitter
|
||||
{
|
||||
Init(addr, port, ipv6, identifier, credentials) {
|
||||
this.m_addr = addr;
|
||||
this.m_identifier = identifier;
|
||||
this.m_credentials = credentials;
|
||||
this.m_connecting = false;
|
||||
this.m_port = port;
|
||||
this.m_ipv6 = ipv6;
|
||||
this.m_encrypted = false;
|
||||
this.m_connection = null;
|
||||
this.m_buffer = Buffer.alloc(0);
|
||||
this.m_public_key_ours = null;
|
||||
this.m_private_key_ours = null;
|
||||
this.m_nonce_ours = null;
|
||||
this.m_public_key_theirs = null;
|
||||
this.m_nonce_theirs = null;
|
||||
this.m_shared_key = null;
|
||||
|
||||
var self = this;
|
||||
setInterval(function() { self.Connect(); }, 100);
|
||||
}
|
||||
|
||||
Send(opcode, p) {
|
||||
try {
|
||||
var out;
|
||||
if(this.m_encrypted) {
|
||||
if(p.length == 0) {
|
||||
p = Buffer.alloc(1);
|
||||
}
|
||||
|
||||
out = Buffer.alloc(6);
|
||||
out.writeUInt32LE(p.length + sodium.crypto_secretbox_MACBYTES, 0);
|
||||
out.writeUInt16LE(opcode, 4);
|
||||
|
||||
var cipher = sodium.crypto_box_easy_afternm(p, this.m_nonce_ours, this.m_shared_key);
|
||||
this.IncrementUint64(this.m_nonce_ours);
|
||||
|
||||
out = Buffer.concat([out, Buffer.from(cipher)], out.length + cipher.length);
|
||||
} else {
|
||||
out = Buffer.alloc(6);
|
||||
out.writeUInt32LE(p.length, 0);
|
||||
out.writeUInt16LE(opcode, 4);
|
||||
out = Buffer.concat([out, p], out.length + p.length);
|
||||
}
|
||||
|
||||
this.InternalSend(ServertalkPacketType.ServertalkMessage, out);
|
||||
} catch(ex) {
|
||||
this.emit('error', new Error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
Connected() {
|
||||
return this.m_connection && !this.m_connecting;
|
||||
}
|
||||
|
||||
Connect() {
|
||||
if (this.m_port == 0 || this.m_connection || this.m_connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.m_connecting = true;
|
||||
|
||||
this.emit('connecting');
|
||||
|
||||
var self = this;
|
||||
this.m_connection = net.connect({port: this.m_port, host: this.m_addr}, function() {
|
||||
self.m_connection.on('close', function(had_error) {
|
||||
self.emit('close');
|
||||
self.m_connection = null;
|
||||
self.m_encrypted = false;
|
||||
});
|
||||
|
||||
self.m_connection.on('data', function(buffer) {
|
||||
self.ProcessData(buffer);
|
||||
});
|
||||
|
||||
self.SendHello();
|
||||
self.m_connecting = false;
|
||||
});
|
||||
|
||||
this.m_connection.on('error', function() {
|
||||
self.emit('close');
|
||||
self.m_connection = null;
|
||||
self.m_connecting = false;
|
||||
});
|
||||
}
|
||||
|
||||
ProcessData(buffer) {
|
||||
this.m_buffer = Buffer.concat([this.m_buffer, buffer], this.m_buffer.length + buffer.length);
|
||||
this.ProcessReadBuffer();
|
||||
}
|
||||
|
||||
SendHello() {
|
||||
var p = Buffer.alloc(0);
|
||||
this.InternalSend(ServertalkPacketType.ServertalkClientHello, p);
|
||||
}
|
||||
|
||||
InternalSend(type, p) {
|
||||
if(!this.m_connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
var out = Buffer.alloc(5);
|
||||
out.writeUInt32LE(p.length, 0);
|
||||
out.writeUInt8(type, 4);
|
||||
|
||||
if (p.length > 0) {
|
||||
out = Buffer.concat([out, p], out.length + p.length);
|
||||
}
|
||||
|
||||
this.m_connection.write(out);
|
||||
}
|
||||
|
||||
ProcessReadBuffer() {
|
||||
var current = 0;
|
||||
var total = this.m_buffer.length;
|
||||
|
||||
while (current < total) {
|
||||
var left = total - current;
|
||||
|
||||
var length = 0;
|
||||
var type = 0;
|
||||
if (left < 5) {
|
||||
break;
|
||||
}
|
||||
|
||||
length = this.m_buffer.readUInt32LE(current);
|
||||
type = this.m_buffer.readUInt8(current + 4);
|
||||
|
||||
if (current + 5 + length > total) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
var p = Buffer.alloc(0);
|
||||
switch (type) {
|
||||
case ServertalkPacketType.ServertalkServerHello:
|
||||
this.ProcessHello(p);
|
||||
break;
|
||||
case ServertalkPacketType.ServertalkMessage:
|
||||
this.ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
var p = this.m_buffer.slice(current + 5, current + 5 + length);
|
||||
switch (type) {
|
||||
case ServertalkPacketType.ServertalkServerHello:
|
||||
this.ProcessHello(p);
|
||||
break;
|
||||
case ServertalkPacketType.ServertalkMessage:
|
||||
this.ProcessMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
current += length + 5;
|
||||
}
|
||||
|
||||
if (current == total) {
|
||||
this.m_buffer = Buffer.alloc(0);
|
||||
}
|
||||
else {
|
||||
this.m_buffer = this.m_buffer.slice(current);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessHello(p) {
|
||||
this.m_encrypted = false;
|
||||
this.m_public_key_ours = null;
|
||||
this.m_public_key_theirs = null;
|
||||
this.m_private_key_ours = null;
|
||||
this.m_nonce_ours = null;
|
||||
this.m_nonce_theirs = null;
|
||||
this.m_shared_key = null;
|
||||
|
||||
try {
|
||||
var enc = p.readUInt8(0) == 1 ? true : false;
|
||||
if (enc) {
|
||||
if (p.length == (1 + sodium.crypto_box_PUBLICKEYBYTES + sodium.crypto_box_NONCEBYTES)) {
|
||||
this.m_public_key_theirs = p.slice(1, 1 + sodium.crypto_box_PUBLICKEYBYTES);
|
||||
this.m_nonce_theirs = p.slice(1 + sodium.crypto_box_PUBLICKEYBYTES, 1 + sodium.crypto_box_PUBLICKEYBYTES + sodium.crypto_box_NONCEBYTES);
|
||||
this.m_encrypted = true;
|
||||
this.SendHandshake(false);
|
||||
|
||||
this.emit('connect');
|
||||
}
|
||||
else {
|
||||
this.emit('error', new Error('Could not process hello, size !=', 1 + sodium.crypto_box_PUBLICKEYBYTES + sodium.crypto_box_NONCEBYTES));
|
||||
}
|
||||
} else {
|
||||
this.SendHandshake(false);
|
||||
|
||||
this.emit('connect');
|
||||
}
|
||||
} catch(ex) {
|
||||
this.emit('error', new Error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
ProcessMessage(p) {
|
||||
try {
|
||||
var length = this.m_buffer.readUInt32LE(0);
|
||||
var opcode = this.m_buffer.readUInt16LE(4);
|
||||
if(length > 0) {
|
||||
var data = p.slice(6);
|
||||
|
||||
if(this.m_encrypted) {
|
||||
var message_len = length - sodium.crypto_secretbox_MACBYTES;
|
||||
|
||||
var decrypted = sodium.crypto_box_open_easy_afternm(data, this.m_nonce_theirs, this.m_shared_key);
|
||||
|
||||
this.IncrementUint64(this.m_nonce_theirs);
|
||||
|
||||
this.emit('message', opcode, decrypted);
|
||||
} else {
|
||||
this.emit('message', opcode, data);
|
||||
}
|
||||
} else {
|
||||
this.emit('message', opcode, Buffer.alloc(0));
|
||||
}
|
||||
} catch(ex) {
|
||||
this.emit('error', new Error(ex));
|
||||
}
|
||||
}
|
||||
|
||||
SendHandshake() {
|
||||
var handshake;
|
||||
|
||||
if(this.m_encrypted) {
|
||||
var keypair = sodium.crypto_box_keypair();
|
||||
this.m_public_key_ours = keypair.publicKey;
|
||||
this.m_private_key_ours = keypair.privateKey;
|
||||
this.m_nonce_ours = Buffer.from(sodium.randombytes_buf(sodium.crypto_box_NONCEBYTES));
|
||||
this.m_shared_key = sodium.crypto_box_beforenm(this.m_public_key_theirs, this.m_private_key_ours);
|
||||
|
||||
this.m_public_key_theirs = null;
|
||||
this.m_private_key_ours = null;
|
||||
|
||||
var message = Buffer.alloc(this.m_identifier.length + this.m_credentials.length + 2);
|
||||
message.write(this.m_identifier, 0);
|
||||
message.write(this.m_credentials, this.m_identifier.length + 1);
|
||||
|
||||
var ciphertext = sodium.crypto_box_easy_afternm(message, this.m_nonce_ours, this.m_shared_key);
|
||||
|
||||
handshake = Buffer.concat([Buffer.from(this.m_public_key_ours), Buffer.from(this.m_nonce_ours), Buffer.from(ciphertext)], sodium.crypto_box_PUBLICKEYBYTES + sodium.crypto_box_NONCEBYTES + ciphertext.length);
|
||||
this.IncrementUint64(this.m_nonce_ours);
|
||||
|
||||
this.m_public_key_ours = null;
|
||||
} else {
|
||||
handshake = Buffer.alloc(this.m_identifier.length + this.m_credentials.length + 2);
|
||||
handshake.write(this.m_identifier, 0);
|
||||
handshake.write(this.m_credentials, this.m_identifier.length() + 1);
|
||||
}
|
||||
|
||||
this.InternalSend(ServertalkPacketType.ServertalkClientHandshake, handshake);
|
||||
}
|
||||
|
||||
IncrementUint64(value) {
|
||||
var bytes = [];
|
||||
for(var i = 0; i < 8; ++i) {
|
||||
bytes[i] = value[i];
|
||||
}
|
||||
|
||||
bytes[0] += 1;
|
||||
for(i = 0; i < 7; ++i) {
|
||||
if(bytes[i] >= 0x100) {
|
||||
bytes[0] = 0;
|
||||
bytes[i + 1] += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(bytes[7] >= 0x100) {
|
||||
bytes[7] = 0;
|
||||
}
|
||||
|
||||
for(var i = 0; i < 8; ++i) {
|
||||
value[i] = bytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
'client': ServertalkClient
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"addr": "localhost",
|
||||
"port": "9101",
|
||||
"key": "ujwn2isnal1987scanb"
|
||||
}
|
||||
Reference in New Issue
Block a user