Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

Commit e35b27da authored by deuce's avatar deuce

Clean up to make syncjslint happier.

Support hashed user IDs as well (some clients don't optionally support them.)
Support Blowfish and IDEA encryption.
parent b20706ee
......@@ -117,10 +117,143 @@ var params = {
336:{type:INTEGER, name:"First Child ID"},
3587:{type:BINARY, name:"Session Key"},
3588:{type:BINARY, name:"MAC Algorithms"},
3777:{type:BINARY, name:"Server Cipher Algorithms"},
3778:{type:BINARY, name:"Client Cipher Algorithms"},
3779:{type:BINARY, name:"Server Cipher Mode"},
3780:{type:BINARY, name:"Client Cipher Mode"},
3781:{type:BINARY, name:"Server Cipher IV"},
3782:{type:BINARY, name:"Client Cipher IV"},
};
var client_version = 0;
var next_id = 1;
var ecc;
var ecc_key;
var dcc;
var dcc_key;
var dcc_blocksize = 1;
var best_hmac;
var session_key;
var decrypted_bytes = 0;
var encrypted_tail = '\x00\x00\x00\x00\x00\x00\x00\x00';
var decrypted_tail = '\x00\x00\x00\x00\x00\x00\x00\x00';
var rekey_rounds = 0;
var rekey_iv = '';
function parse_user_line(line)
{
var ret = {};
var m = line.match(/^(.*) ([0-9]+) ([0-9]+)$/);
if (m != null) {
ret.alias = m[1];
ret.icon = parseInt(m[2], 10);
ret.flags = parseInt(m[3], 10);
ret.id = system.matchuser(ret.alias);
}
return ret;
}
function remove_online_user(username)
{
var i;
var f = new File(system.temp_dir+'hotline.users');
var found = false;
if (f.open('r+b')) {
var lines = f.readAll(2048);
for (i = 0; i<lines.length; i++) {
var u = parse_user_line(lines[i]);
if(u.id & 0x8000)
continue;
if (u.alias == username) {
found = true;
lines.splice(i, 1);
i--;
}
}
if (found) {
f.rewind();
f.truncate();
f.writeAll(lines);
}
f.close();
}
file_remove(system.temp_dir+'hotline.chat.'+usr.number);
}
function get_param_type(id)
{
if (params[id] == undefined)
return BINARY;
return params[id].type;
}
function send_message(type, args, is_reply, id, error)
{
var outbuf='';
var i;
var tmp;
if (is_reply === undefined)
is_reply = false;
if (id === undefined) {
if (is_reply)
rage_quit("Reply with no ID specified!");
id = next_id;
}
if (error == undefined)
error = 0;
log(LOG_DEBUG, "Message type: "+ type);
for (i=0; i<args.length; i++) {
outbuf += encode_integer(args[i].id);
if (args[i].id == 100 && error)
log(LOG_DEBUG, "Error: "+args[i].value);
else
log(LOG_DEBUG, "Parameter: "+args[i].id);
switch (get_param_type(args[i].id)) {
case BINARY:
case STRING:
tmp = args[i].value;
break;
case INTEGER:
tmp = encode_integer(args[i].value);
break;
}
outbuf += encode_integer(tmp.length);
outbuf += tmp;
}
outbuf = encode_short(args.length) + outbuf;
tmp = outbuf.length;
outbuf = encode_long(tmp) + outbuf; // Data size
outbuf = encode_long(tmp) + outbuf; // Total size
outbuf = encode_long(error) + outbuf; // Error
outbuf = encode_long(id) + outbuf; // Message ID
outbuf = encode_short(type) + outbuf; // Type
outbuf = ascii(is_reply?1:0) + outbuf; // Is Reply
// TODO: Rekey randomly...
outbuf = ascii(0) + outbuf; // Flags
if (ecc != undefined)
outbuf = ecc.encrypt(outbuf);
client.socket.send(outbuf);
if (!is_reply) {
next_id++;
if (next_id == 0 || next_id >= 1<<32)
next_id = 1;
}
}
function rage_quit(reason)
{
log(LOG_DEBUG, "Rage quit: "+reason);
if (usr != undefined)
remove_online_user(usr.alias);
send_message(111, [{id:101, value:reason}]);
client.socket.close();
exit();
}
function add_privs(privs, ret)
{
......@@ -144,11 +277,35 @@ function add_privs(privs, ret)
return ret;
}
function get_param_type(id)
function read_online_users()
{
if (params[id] == undefined)
return BINARY;
return params[id].type;
var ret = [];
var i;
var f = new File(system.temp_dir+'hotline.users');
if (f.open('rb')) {
var lines = f.readAll(2048);
f.close();
for (i in lines) {
ret.push(parse_user_line(lines[i]));
}
}
// Now add users who are on the BBS
for (i in system.node_list) {
if ((system.node_list[i].status == NODE_INUSE) || (usr.is_sysop && system.node_list[i].status == NODE_QUIET)) {
var uo = new User(system.node_list[i].useron);
if (uo != null) {
var usro = {node:i, alias:uo.alias, icon:412, flags:0x01};
if (uo.is_sysop)
usro.flags |= 0x02;
usro.id = uo.number | 0x8000;
ret.push(usro);
}
}
}
return ret;
}
function update_data()
......@@ -157,6 +314,7 @@ function update_data()
var found;
var params;
var new_usrs = read_online_users();
var alias;
var msg = system.get_telegram(usr.number);
if (msg != null) {
......@@ -169,7 +327,7 @@ function update_data()
var m = msg.match(/^Hotline message from ([^:]+):\r([\x00-\xff]*)$/);
if (m != null) {
params.push({id:102, value:m[1]});
var alias = m[1];
alias = m[1];
msg = m[2];
for (i in new_usrs) {
if (alias == new_usrs[i].alias && new_usrs[i].id < 0x8000)
......@@ -190,7 +348,7 @@ function update_data()
for (i in cmsgs) {
m = cmsgs[i].match(/^([0-9]+) ([0-9]+) ([\x00-\xff]*)$/);
if (m != null) {
var alias = new User(parseInt(m[2], 10)).alias;
alias = new User(parseInt(m[2], 10)).alias;
params = [{id:101, value:'\r'+alias+': '+m[3]}, {id:103, value:parseInt(m[2])}];
var chatid = parseInt(m[1], 10);
if (chatid)
......@@ -232,6 +390,71 @@ function update_data()
usrs = new_usrs;
}
function do_decrypt(val)
{
if (val.length == 0)
return val;
encrypted_tail += val;
val = dcc.decrypt(val);
decrypted_tail += val;
decrypted_bytes += val.length;
while(decrypted_bytes >= dcc_blocksize) {
decrypted_bytes -= dcc_blocksize;
encrypted_tail = encrypted_tail.substr(dcc_blocksize);
decrypted_tail = decrypted_tail.substr(dcc_blocksize);
}
return val;
}
function derive_old_key()
{
var i;
var oldiv='';
for (i=0; i<dcc_blocksize; i++)
oldiv += ascii(ascii(encrypted_tail.substr(i, 1)) ^ ascii(decrypted_tail.substr(i, 1)));
return oldiv;
}
function rekey(rounds)
{
var oldiv;
var i;
oldiv = derive_old_key();
for (i=0; i<rounds; i++)
dcc_key = best_hmac(dcc_key, session_key);
dcc = new CryptContext(dcc.algo);
dcc.mode = CryptContext.MODE.OFB;
dcc.iv = oldiv;
dcc.set_key(dcc_key);
}
function readsock_bytes(sock, size)
{
var val1 = '';
var val2 = '';
if (dcc != undefined && rekey_rounds) {
if (size >= (dcc_blocksize - decrypted_bytes)) {
val1 = sock.recv(dcc_blocksize - decrypted_bytes);
size -= (dcc_blocksize - decrypted_bytes);
val1 = do_decrypt(val1);
rekey(rekey_rounds);
rekey_rounds = 0;
}
}
val2 = sock.recv(size);
if (dcc != undefined)
val2 = do_decrypt(val2);
return val1 + val2;
}
function readsock_integer(sock, size)
{
return decode_integer(readsock_bytes(sock, size));
}
function read_msg(sock)
{
var i;
......@@ -249,19 +472,30 @@ function read_msg(sock)
if (!sock.is_connected)
return msg;
msg.hdr.flags = sock.recvBin(1);
msg.hdr.is_reply = sock.recvBin(1);
msg.hdr.type = sock.recvBin(2);
msg.hdr.flags = readsock_integer(sock, 1);
log(LOG_DEBUG, "Flags: "+msg.hdr.flags);
msg.hdr.is_reply = readsock_integer(sock, 1);
msg.hdr.type = readsock_integer(sock, 2);
log(LOG_DEBUG, "Type: "+msg.hdr.type);
msg.hdr.id = sock.recvBin(4);
msg.hdr.err = sock.recvBin(4);
msg.hdr.total_size = sock.recvBin(4);
msg.hdr.data_size = sock.recvBin(4);
msg.param_count = sock.recvBin(2);
msg.hdr.id = readsock_integer(sock, 4);
msg.hdr.err = readsock_integer(sock, 4);
msg.hdr.total_size = readsock_integer(sock, 4);
msg.hdr.data_size = readsock_integer(sock, 4);
msg.param_count = readsock_integer(sock, 2);
if (msg.hdr.flags && dcc != undefined) {
// Re-key...
if (decrypted_bytes == 0) {
rekey(msg.hdr.flags);
}
else {
rekey_rounds = msg.hdr.flags;
log(LOG_DEBUG, "Rekeying with "+rekey_rounds+" rounds in "+(dcc_blocksize - decrypted_bytes)+" bytes");
}
}
log(LOG_DEBUG, "Params: "+msg.param_count);
msg.params={};
for (i=0; i<msg.param_count; i++) {
var id = sock.recvBin(2);
var id = readsock_integer(sock, 2);
var param;
log(LOG_DEBUG, "Getting parameter ID: "+id);
......@@ -275,79 +509,15 @@ log(LOG_DEBUG, "Getting parameter ID: "+id);
msg.params[id]={};
param = msg.params[id];
}
param.size = sock.recvBin(2);
param.size = readsock_integer(sock, 2);
if (get_param_type(id)==INTEGER)
param.data = sock.recvBin(param.size);
param.data = readsock_integer(sock, param.size);
else
param.data = sock.recv(param.size);
param.data = readsock_bytes(sock, param.size);
}
return msg;
}
function rage_quit(reason)
{
log(LOG_DEBUG, "Rage quit: "+reason);
if (usr != undefined)
remove_online_user(usr.alias);
send_message(111, [{id:101, value:reason}]);
client.socket.close();
exit();
}
function send_message(type, args, is_reply, id, error)
{
var outbuf='';
var i;
var tmp;
if (is_reply === undefined)
is_reply = false;
if (id === undefined) {
if (is_reply)
rage_quit("Reply with no ID specified!");
id = next_id;
}
if (error == undefined)
error = 0;
log(LOG_DEBUG, "Message type: "+ type);
for (i=0; i<args.length; i++) {
outbuf += encode_integer(args[i].id);
if (args[i].id == 100 && error)
log(LOG_DEBUG, "Error: "+args[i].value);
else
log(LOG_DEBUG, "Parameter: "+args[i].id);
switch (get_param_type(args[i].id)) {
case BINARY:
case STRING:
tmp = args[i].value;
break;
case INTEGER:
tmp = encode_integer(args[i].value);
break;
}
outbuf += encode_integer(tmp.length);
outbuf += tmp;
}
outbuf = encode_short(args.length) + outbuf;
tmp = outbuf.length;
outbuf = encode_long(tmp) + outbuf; // Data size
outbuf = encode_long(tmp) + outbuf; // Total size
outbuf = encode_long(error) + outbuf; // Error
outbuf = encode_long(id) + outbuf; // Message ID
outbuf = encode_short(type) + outbuf; // Type
outbuf = ascii(is_reply?1:0) + outbuf; // Is Reply
outbuf = ascii(0) + outbuf; // Flags
client.socket.send(outbuf);
if (!is_reply) {
next_id++;
if (next_id == 0 || next_id >= 1<<32)
next_id = 1;
}
}
function send_response(hdr, args, error)
{
return send_message(0, args, true, hdr.id, error);
......@@ -425,6 +595,7 @@ function setup_transfer(path)
var f = new File(system.temp_dir+'hotline.transfers');
var ret=undefined;
var i;
var m;
if (f.open("a+b")) {
f.rewind();
......@@ -448,19 +619,6 @@ function setup_transfer(path)
return ret;
}
function parse_user_line(line)
{
var ret = {};
var m = line.match(/^(.*) ([0-9]+) ([0-9]+)$/);
if (m != null) {
ret.alias = m[1];
ret.icon = parseInt(m[2], 10);
ret.flags = parseInt(m[3], 10);
ret.id = system.matchuser(ret.alias);
}
return ret;
}
function update_online_user(username, icon, flags)
{
var i;
......@@ -494,65 +652,6 @@ function update_online_user(username, icon, flags)
rage_quit("Unable to open user file!");
}
function read_online_users()
{
var ret = [];
var i;
var f = new File(system.temp_dir+'hotline.users');
if (f.open('rb')) {
var lines = f.readAll(2048);
f.close();
for (i in lines) {
ret.push(parse_user_line(lines[i]));
}
}
// Now add users who are on the BBS
for (i in system.node_list) {
if ((system.node_list[i].status == NODE_INUSE) || (usr.is_sysop && system.node_list[i].status == NODE_QUIET)) {
var uo = new User(system.node_list[i].useron);
if (uo != null) {
var usro = {node:i, alias:uo.alias, icon:412, flags:0x01};
if (uo.is_sysop)
usro.flags |= 0x02;
usro.id = uo.number | 0x8000;
ret.push(usro);
}
}
}
return ret;
}
function remove_online_user(username)
{
var i;
var f = new File(system.temp_dir+'hotline.users');
found = false;
if (f.open('r+b')) {
var lines = f.readAll(2048);
for (i = 0; i<lines.length; i++) {
var u = parse_user_line(lines[i]);
if(u.id & 0x8000)
continue;
if (u.alias == username) {
found = true;
lines.splice(i, 1);
i--;
}
}
if (found) {
f.rewind();
f.truncate();
f.writeAll(lines);
}
f.close();
}
file_remove(system.temp_dir+'hotline.chat.'+usr.number);
}
// TODO: modopts.ini thingie.
var include_real_name = false;
var include_age_gender = false;
......@@ -645,7 +744,7 @@ function send_chat_msg(id, opt, msg)
}
}
function parse_mac_algs(alg_list)
function parse_login_list(alg_list)
{
var i;
var ret = [];
......@@ -653,11 +752,14 @@ function parse_mac_algs(alg_list)
var len;
alg_list = alg_list.substr(2);
log(LOG_DEBUG, "Total algs: "+total_algs);
for (i=0; i<total_algs; i++) {
len = ascii(alg_list.substr(0, 1));
ret.push(alg_list.substr(1, len));
alg_list = alg_list.substr(len+1);
}
for (i in ret)
log(LOG_DEBUG, "Got alg: "+ret[i]);
return ret;
}
......@@ -672,7 +774,6 @@ function generate_session_key()
return ret;
}
function log_bin(str)
{
var os = '';
......@@ -684,20 +785,42 @@ function log_bin(str)
log(LOG_DEBUG, "Binary data: "+os);
}
function decode_string(str)
{
var i;
var ret='';
for (i=0; i<str.length; i++) {
ret += ascii(ascii(str.substr(i, 1)) ^ 255);
}
return ret;
}
function handle_message(msg)
{
var i, j, k;
var ma, fmt;
var f, message;
var days, hours, min, seconds, mins, ut;
var chatid, chatstr, chatopt;
var username, password, macalgs, cryptalgs;
var hmac_type, best_crypt, crypt_type, uid, tmpusr, agreementf, tmp;
var userid, new_usrs;
var depth;
var mb;
var body;
var hdr;
switch(msg.hdr.type) {
case 101: // Get Message...
var f=new File(system.text_dir+"welcome.msg");
var message = "Welcome to "+system.name;
f=new File(system.text_dir+"welcome.msg");
message = "Welcome to "+system.name;
if(f.open("rb",true)) {
message = f.read();
f.close();
message=ascii_str(message).replace(/@([^@]*)@/g,
function(matched, code) {
var fmt="%s";
fmt="%s";
ma=new Array();
if((ma=code.match(/^(.*)-L.*$/))!=undefined) {
fmt="%-"+(code.length)+"s";
......@@ -719,17 +842,17 @@ function handle_message(msg)
case 'OS_VER':
return(format(fmt,system.os_version.toString()));
case 'UPTIME':
var days=0;
var hours=0;
var min=0;
var seconds=0;
var ut=time()-system.uptime;
days=0;
hours=0;
min=0;
seconds=0;
ut=time()-system.uptime;
days=(ut/86400);
ut%=86400;
hours=(ut/3600);
ut%=3600;
mins=(ut/60);
secsonds=parseInt(ut%60);
seconds=parseInt(ut%60);
if(parseInt(days)!=0)
ut=format("%d days %d:%02d",days,hours,mins);
else
......@@ -775,9 +898,9 @@ function handle_message(msg)
send_response(msg.hdr, [{id:101, value:message}]);
break;
case 105: // Send Chat
var chatid = 0;
var chatstr = '';
var chatopt = 0;
chatid = 0;
chatstr = '';
chatopt = 0;
if (usr.compare_ars("REST C")) {
send_response(msg.hdr, [{id:100, value:"You are not allowed to chat"}], 1);
......@@ -792,14 +915,19 @@ function handle_message(msg)
send_chat_msg(chatid, chatopt, chatstr);
break;
case 107: // Login
var username='';
var password='';
username='';
password='';