diff --git a/js/dos/hostsrv.js b/js/dos/hostsrv.js new file mode 100644 index 0000000..eee6f1d --- /dev/null +++ b/js/dos/hostsrv.js @@ -0,0 +1,778 @@ +/** + ** ============================== + ** OOOO O O OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O OOO OOOO + ** O O O O O O O + ** O O O O O O O + ** OOOO OOOO O O OOOO + ** ============================== + ** Dr. Stefan Bosse http://www.bsslab.de + ** + ** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED + ** BY THE AUTHOR(S). + ** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED, + ** MODIFIED, OR OTHERWISE USED IN A CONTEXT + ** OUTSIDE OF THE SOFTWARE SYSTEM. + ** + ** $AUTHORS: Stefan Bosse + ** $INITIAL: (C) 2006-2016 BSSLAB + ** $CREATED: 5/25/15 by sbosse. + ** $VERSION: 1.3.11 + ** + ** $INFO: + ** + ** DOS: Host Server + ** + * The HOST Server listening on the public host port. + * Provides a simple plain DNS (one root directory) used for publishing local servers. + * + * Basic directory structure: + * + * / + * /hosts + * /domains + * /dns + * + ** + ** $ENDOFINFO + */ + +var log = 0; +var util = Require('util'); +var Io = Require('com/io'); +var trace = Io.tracing; + + +var Net = Require('dos/network'); +var Rpc = Require('dos/rpc'); +var Router = Require('dos/router'); +var Comp = Require('com/compat'); +var Perv = Comp.pervasives; +var String = Comp.string; +var Array = Comp.array; +var assert = Comp.assert; +var Sch = Require('dos/scheduler'); +var Buf = Require('dos/buf'); +var DnsCom = Require('dos/dns_srv_common'); +var DnsEMB = Require('dos/dns_srv_emb'); +var Dns = Require('dos/dns'); +var Cs = Require('dos/capset'); +var Status = Net.Status; +var Command = Net.Command; +var Rights = Net.Rights; + +var isNodeJS = Comp.isNodeJS(); + + + +/** Create a HOST server with embedded DNS + * + * @param {rpcint} rpc + * @param {{pubhostport,privhostport,verbose}} options + * @param {string} [name] + * @param {object} [env] + * @constructor + */ +var hostserver = function (rpc,options,name,env) { + var main = this, + router = rpc.router + + this.env=env||{}; + this.options=options||{verbose:0}; + + this.CsInt = Cs.CsInt(rpc); + this.name=name||Net.Print.port(options.pubhostport); + this.out=function(msg) {(options.out||Io.out)('[HOST '+main.name+'] '+msg)}; + this.err=function(msg) {(options.out||Io.out)('[HOST '+main.name+'] Error: '+msg)}; + + /* + ** Simple DNS (only one root directory) table + */ + this.random=Net.uniqport(); + this.dns = Dns.DnsInt(rpc); + this.dnsSrv = DnsEMB.Server(rpc); + this.dnsSrv.create_dns(options.pubhostport,options.privhostport,this.random,false,Dns.DNS_DEFAULT_COLS); + + if (this.options.verbose>0) this.out('My host port is '+Net.Print.port(router.hostport)); + if (this.options.verbose>0) this.out('My host name is '+this.name); + if (this.options.verbose>0) this.out('DNS Root is '+Net.Print.capability(this.CsInt.cs_to_cap(this.dnsSrv.rootcs))); + + this.hostcap = Net.Capability(options.pubhostport,Net.Private(0,Rights.PRV_ALL_RIGHTS,this.random)); + this.info='HOST '+this.name; + + if (env && !env.rootdir) env.rootdir=this.dnsSrv.rootcs; + this.rootcs=this.dnsSrv.rootcs; + + // Server + + this.services = []; + this.dns_rights = [ + Rights.DNS_RGT_OWNER|Rights.DNS_RGT_GROUP|Rights.DNS_RGT_OTHERS| + Rights.DNS_RGT_READ|Rights.DNS_RGT_MODIFY|Rights.DNS_RGT_DELETE|Rights.DNS_RGT_CREATE, + Rights.DNS_RGT_GROUP|Rights.DNS_RGT_OTHERS| + Rights.DNS_RGT_READ, + Rights.DNS_RGT_OTHERS| + Rights.DNS_RGT_READ + ]; + this.host_rights = [ + Rights.HOST_INFO|Rights.HOST_READ|Rights.HOST_WRITE|Rights.HOST_EXEC, + Rights.HOST_INFO|Rights.HOST_READ|Rights.HOST_WRITE|Rights.HOST_EXEC, + Rights.HOST_INFO + ]; + + this.afs_rights = [ + Rights.AFS_RGT_ADMIN|Rights.AFS_RGT_CREATE|Rights.AFS_RGT_DESTROY|Rights.AFS_RGT_MODIFY|Rights.AFS_RGT_READ, + Rights.AFS_RGT_READ, + Rights.AFS_RGT_READ + ]; + + /* + ** Create initial directory structure... + */ + main.hostsdir=main.dnsSrv.create_dir(Dns.DNS_DEFAULT_COLS); + main.domainsdir=main.dnsSrv.create_dir(Dns.DNS_DEFAULT_COLS); + main.dnsdir=main.dnsSrv.create_dir(Dns.DNS_DEFAULT_COLS); + main.afsdir=main.dnsSrv.create_dir(Dns.DNS_DEFAULT_COLS); + main.rootdir=main.dnsSrv.rootdir; + + main.dnsSrv.append_row(main.hostsdir, + DnsCom.Dns_row(main.name,main.dnsSrv.time(),this.host_rights, + main.CsInt.cs_singleton(main.hostcap))); + + main.dnsSrv.append_row(main.dnsSrv.rootdir,DnsCom.Dns_row('hosts',main.dnsSrv.time(),this.dns_rights, + main.dnsSrv.capset_of_dir(main.hostsdir))); + main.dnsSrv.append_row(main.dnsSrv.rootdir,DnsCom.Dns_row('domains',main.dnsSrv.time(),this.dns_rights, + main.dnsSrv.capset_of_dir(main.domainsdir))); + main.dnsSrv.append_row(main.dnsSrv.rootdir,DnsCom.Dns_row('dns',main.dnsSrv.time(),this.dns_rights, + main.dnsSrv.capset_of_dir(main.dnsdir))); + main.dnsSrv.append_row(main.dnsSrv.rootdir,DnsCom.Dns_row('afs',main.dnsSrv.time(),this.dns_rights, + main.dnsSrv.capset_of_dir(main.afsdir))); + main.dnsSrv.release_dir(main.hostsdir); + main.dnsSrv.release_dir(main.domainsdir); + main.dnsSrv.release_dir(main.dnsdir); + main.dnsSrv.release_dir(main.afsdir); + + main.geo=options.geo||{}; + + this.thread = function (arg) { + var thr = this, + dying=false, + rpcio2, + rpcio; + + this.init = function () { + rpcio = router.pkt_get(); + if (main.options.verbose>0) main.out('Starting host server with public port ' + + Net.Print.port(main.options.pubhostport)); + router.add_port(main.options.privhostport); + }; + + this.request = function () { + Io.log((log < 1) || ('[HOST'+arg+'] waiting for a request')); + rpcio.init(); + rpcio.operation = Rpc.Operation.GETREQ; + rpcio.header.h_port = main.options.privhostport; + rpcio.header.h_status = undefined; + rpcio.header.h_command = undefined; + rpcio.header.h_priv = undefined; + // Io.out(util.inspect(rpcio)); + rpc.getreq(rpcio); + assert((rpcio.index != -1) || ('RPCIO invalid')); + }; + + this.service = function () { + var mem, str, used, i, service, name, path, cs, rights, ncols, cols, colnames, + found, off, mask, priv, paramn, params, stat, serviced; + + Io.log((log < 1) || ('[HOST'+arg+'] service request')); + assert(rpcio.index != -1, 'RPCIO invalid'); + Io.log((log < 1) || (Net.Print.header(rpcio.header))); + + rpcio.pos = 0; + rpcio.header.h_status = Status.STD_OK; + var obj = Net.prv_number(rpcio.header.h_priv); + + switch (rpcio.header.h_command) { + /* + ** Standard Calls + */ + case Command.STD_INFO: + Io.log((log < 1) || 'hostsrv.STD_INFO ' + Net.Print.private(rpcio.header.h_priv)); + Buf.buf_init(rpcio); + if (obj == 0) Buf.buf_put_string(rpcio, main.info); + else { + // It is a directory object. + main.dnsSrv.dns_info(rpcio.header.h_priv, function (_stat, _str) { + stat=_stat; + if (stat==Status.STD_OK) Buf.buf_put_string(rpcio, _str); + else rpcio.header.h_status=stat; + }); + } + break; + + case Command.STD_AGE: + /* + ** Force memory garbage collection! + */ + if (isNodeJS && global.gc != undefined) { + mem = process.memoryUsage(); + var mem0 = mem; + var progress = 1000; + var count = 20; + while (count > 0 && progress >= 1000) { + global.gc(); + mem = process.memoryUsage(); + progress = mem0.rss - mem.rss; + if (progress < 0) progress = mem0.heapUsed - mem.heapUsed; + mem0 = mem; + count--; + } + } + else rpcio.header.h_status = Status.STD_SYSERR; + break; + + case Command.STD_STATUS: + Io.log((log < 1) || 'hostsrv.STD_STATUS ' + Net.Print.private(rpcio.header.h_priv)); + Buf.buf_init(rpcio); + if (obj == 0) { + + str = 'Statistics\n==========\n'; + if (process != undefined) { + mem = process.memoryUsage(); + str = str + 'MEMORY: RSS=' + Perv.div(mem.rss, 1024) + ' HEAP=' + Perv.div(mem.heapTotal, 1024) + + ' USED=' + Perv.div(mem.heapUsed, 1024) + ' kB\n'; + } + str = str + router.status()+'\n'; + Buf.buf_put_string(rpcio, str); + } else { + // It is a directory object. + main.dnsSrv.dns_stat(rpcio.header.h_priv, function (_stat, _str) { + stat=_stat; + if (stat==Status.STD_OK) Buf.buf_put_string(rpcio, _str); + else rpcio.header.h_status=stat; + }); + + } + break; + + case Command.STD_LOCATION: + break; + + case Command.STD_RESTRICT: + /* + * ---------- + * mask (int16) + * ---------- + * priv (privat) + * ---------- + */ + Io.log((log < 1) || 'hostsrv.STD_RESTRICT ' + Net.Print.private(rpcio.header.h_priv)); + mask=Buf.buf_get_int16(rpcio); + Buf.buf_init(rpcio); + priv=Net.restrict(rpcio.header.h_priv,mask,main.random); + if (priv!=undefined) { + Buf.buf_put_priv(rpcio,priv); + rpcio.header.h_status=Status.STD_OK; + } else rpcio.header.h_status=Status.STD_SYSERR; + break; + + case Command.STD_SETPARAMS: + Io.log((log < 1) || 'hostsrv.STD_SETPARAMS ' + Net.Print.private(rpcio.header.h_priv)); + paramn=Buf.buf_get_int16(rpcio); + params = []; + stat=Status.STD_OK; + if (paramn > 0 && paramn < 256) { + for (i = 0; i < paramn; i++) { + var pn = Buf.buf_get_string(rpcio); + var pv = Buf.buf_get_string(rpcio); + params.push([pn,pv]); + } + } else stat=Status.STD_ARGBAD; + Array.iter (params,function (param) { + String.match(param[0], [ + ['monitor',function () { + router.monitor=Perv.int_of_string(param[1]); + }] + ]) + }); + Buf.buf_init(rpcio); + rpcio.header.h_status=stat; + break; + + /********************* + ** DNS Calls + **********************/ + case Command.DNS_APPEND: + /* + * ------------ + * name:string + * obj:capset + * ncols:number + * ------------ + * ------------ + */ + Io.log((log < 1) || 'hostsrv.DNS_APPEND ' + Net.Print.private(rpcio.header.h_priv)); + name = Buf.buf_get_string(rpcio); + cs = Cs.buf_get_capset(rpcio); + ncols = Buf.buf_get_int16(rpcio); + cols = []; + for (i = 0; i < ncols; i++) { + cols.push(Dns.buf_get_rights(rpcio)); + } + if (log > 1) { + Io.inspect(name); + Io.inspect(cs); + Io.inspect(cols); + } + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_append(rpcio.header.h_priv, name, cols, cs, function (_stat) { + rpcio.header.h_status = _stat; + Buf.buf_init(rpcio); + Io.log((log < 1) || ('hostsrv.DNS_APPEND returns: ' + Net.Status.print(_stat))); + }); + }]); + Io.log((log < 1) || ('hostsrv.DNS_APPEND passes through')); + break; + + + case Command.DNS_LOOKUP: + Io.log((log < 1) || 'hostsrv.DNS_LOOKUP ' + Net.Print.private(rpcio.header.h_priv)); + path = Buf.buf_get_string(rpcio); + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_lookup(rpcio.header.h_priv, path, function (_stat, _cs, _path) { + Io.log((log < 1) || ('hostsrv.DNS_LOOKUP returns: ' + Net.Status.print(_stat)+', '+_path)); + Buf.buf_init(rpcio); + if (_stat == Status.STD_OK) { + /* + ** Put the path_rest string and the capability set. + */ + Buf.buf_put_string(rpcio, _path); + Cs.buf_put_capset(rpcio, _cs); + + } else { + rpcio.header.h_status = _stat; + } + }); + }]); + Io.log((log < 1) || ('hostsrv.DNS_LOOKUP passes through')); + break; + + case Command.DNS_RENAME: + /* + * ------------ + * oldname:string + * newname:string + * ------------ + * ------------ + */ + Io.log((log < 1) || 'hostsrv.DNS_RENAME ' + Net.Print.private(rpcio.header.h_priv)); + name = Buf.buf_get_string(rpcio); + var newname = Buf.buf_get_string(rpcio); + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_rename(rpcio.header.h_priv, name, newname, function (_stat) { + Buf.buf_init(rpcio); + rpcio.header.h_status = _stat; + }); + }]); + break; + + case Command.DNS_DELETE: + /* + * ------------ + * rowname:string + * ------------ + * ------------ + */ + Io.log((log < 1) || ('hostsrv.DNS_DELETE ' + Net.Print.private(rpcio.header.h_priv))); + name = Buf.buf_get_string(rpcio); + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_delete(rpcio.header.h_priv, name, function (_stat) { + Buf.buf_init(rpcio); + rpcio.header.h_status = _stat; + }); + }]); + break; + + case Command.DNS_CREATE: + /* + * ----- + * ncols(int16) + * [colname(string)] #ncols + * ----- + * cs(capset) + * ----- + */ + Io.log((log < 1) || 'hostsrv.DNS_CREATE ' + Net.Print.private(rpcio.header.h_priv)); + ncols = Buf.buf_get_int16(rpcio); + colnames = []; + for (i = 0; i < ncols; i++) { + colnames.push(Buf.buf_get_string(rpcio)); + } + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_create(rpcio.header.h_priv, colnames, function (_stat, _cs) { + Buf.buf_init(rpcio); + rpcio.header.h_status = _stat; + if (_stat == Status.STD_OK) { + Cs.buf_put_capset(rpcio, _cs); + } + }); + }]); + break; + + case Command.DNS_LIST: + /* + * -------------- + * rowoff(int16) + * --------------- + * next_row(int16) + * ncols(int16) + * nrows(int16) + * colnames: [name(string)] #ncols + * rows: [ + * name(string) + * time(int32) + * [rights(int16)] #ncols + * ] #nrows + * -------------- + */ + Io.log((log < 1) || 'hostsrv.DNS_LIST ' + Net.Print.private(rpcio.header.h_priv)); + off = Buf.buf_get_int16(rpcio); + rpcio.pos = 0; + Sch.ScheduleBlock([ + function () { + main.dnsSrv.dns_list(rpcio.header.h_priv, off, 1000, + function (_stat, nrows, ncols, colnames, rows) { + rpcio.header.h_status = _stat; + Buf.buf_init(rpcio); + if (_stat == Status.STD_OK) { + Buf.buf_put_int16(rpcio, Dns.DNS_NOMOREROWS); + Buf.buf_put_int16(rpcio, ncols); + Buf.buf_put_int16(rpcio, nrows); + /* + ** Push the column names + */ + Array.iter(colnames, function (col) { + Buf.buf_put_string(rpcio, col); + }); + /* + ** Assumption: all rows fit in one transaction + */ + Array.iter(rows, function (row, index) { + Buf.buf_put_string(rpcio, row[0]); + Buf.buf_put_int32(rpcio, row[1]); + Array.iter(row[2], function (col, index) { + Dns.buf_put_rights(rpcio, col); + }); + }); + } + }); + }]); + break; + + case Command.DNS_SETLOOKUP: + /* + * ------------ + * nrows(int16) + * [ + * dir(capset) + * name(string) + * ] #nrows + * ------------ + * [ + * status(int16) + * cs(capset) + * ] #nrows + * ------------ + */ + Io.log((log < 1) || 'hostsrv.DNS_SETLOOKUP ' + Net.Print.private(rpcio.header.h_priv)); + var nrows = Buf.buf_get_int16(rpcio); + var dirs=[]; + for(i=0;i