initial commits
authorPaul J R <me@pjr.cc>
Tue, 15 Jan 2013 15:18:19 +0000 (02:18 +1100)
committerPaul J R <me@pjr.cc>
Tue, 15 Jan 2013 15:18:19 +0000 (02:18 +1100)
14 files changed:
lxc/appconfig [new file with mode: 0644]
lxc/config [new file with mode: 0644]
lxc/init.js [new file with mode: 0644]
lxc/lxc-nodejs [new file with mode: 0755]
tftp/node_modules/.bin/tftp-client [new file with mode: 0755]
tftp/node_modules/tftp-client/.npmignore [new file with mode: 0644]
tftp/node_modules/tftp-client/README.md [new file with mode: 0644]
tftp/node_modules/tftp-client/TFTP-Client.sublime-project [new file with mode: 0644]
tftp/node_modules/tftp-client/TFTP-Client.sublime-workspace [new file with mode: 0644]
tftp/node_modules/tftp-client/bin/tftp.js [new file with mode: 0755]
tftp/node_modules/tftp-client/lib/tftp.js [new file with mode: 0644]
tftp/node_modules/tftp-client/package.json [new file with mode: 0644]
tftp/tftpd.js [new file with mode: 0644]
tftp/tftpd_1.js [new file with mode: 0644]

diff --git a/lxc/appconfig b/lxc/appconfig
new file mode 100644 (file)
index 0000000..f87b232
--- /dev/null
@@ -0,0 +1,12 @@
+# this is an appconfig file
+# it has the format of:
+# appname:directory:appfilepath[:options]
+# where 
+#      appname is an internally used name to identify the app
+#      directory is the directory to start the app from
+#      appfilepath is the path to the js file to start
+#      options is a set of options such as norestart to not restart application on a failure
+appname1:.:/node/app.js
+appname2:.:/node/app.js
+appname3:.:/node/app.js
+appname4:.:/node/app.js
diff --git a/lxc/config b/lxc/config
new file mode 100644 (file)
index 0000000..aaec2c5
--- /dev/null
@@ -0,0 +1,12 @@
+# there arent many config variables here
+# just ip address, hostname and gateway
+#debug=true
+ip4address=eth0:10.0.3.100/24
+ip4address=lo:127.0.0.1/8
+ip6address=eth0:auto
+hostname=nd
+ip4gateway=10.0.3.1
+ip6gateway=auto
+dnsserver=10.0.3.1
+random=whatever
+checktimer=1000
diff --git a/lxc/init.js b/lxc/init.js
new file mode 100644 (file)
index 0000000..7dc3a5b
--- /dev/null
@@ -0,0 +1,275 @@
+#!/node/bin/node
+
+//var uid=$user
+var fs = require("fs");
+var asroot = false;
+var debug_output = false;
+var default_loop_time = 2000;
+
+// set the global context
+global.lxcnodejs = new Object();
+
+// and begin...
+if(process.getuid() == 0) {
+       asroot = true;
+} else {
+       console.log("Not running as root, assuming debug mode, config file should be ./config");
+}
+
+if(asroot) var mnt = require("/node_modules/mount");
+
+// perform mount
+console.log("Mounting proc");
+if(asroot) {
+       mnt.mount("proc", "/proc", "proc");
+} else {
+       //console.log("fake mount");
+       console.log("(debug) pass /proc mount");
+}
+
+// get the global space setup
+
+
+// read main and node config file and parse
+if(asroot) {
+       console.log("Reading config file /config");
+       loadConfig("/config");
+       loadNodeConfig("/node_base/appconfig", "/node_base");
+} else {
+       console.log("Reading config file ./config (debug mode)");
+       loadConfig("./config");
+       loadNodeConfig("./appconfig", ".");
+}
+
+// begin the main loop
+if(asroot) {
+       startMainLoop();
+} else {
+       if(debug_output) console.log("(debug) main loop now begins with process monitor");
+       startMainLoop();
+}
+
+/*
+ * 
+ * END OF MAIN ROUTINE
+ * 
+ */
+
+function loadNodeConfig(file, basedir) {
+       var configFile = fs.readFileSync(file).toString();
+       var lines = configFile.split("\n");
+       var nodes = new Object();
+       
+       for(var i=0; i < lines.length; i++) {
+               //console.log("line: ", lines[i].trim());
+               var sks = lines[i].match(/^[^#][a-zA-Z0-9:]+/);
+               var lks = null;
+               if(sks != null) lks = lines[i].split(":");
+               if(lks!=null) {
+                       stl = lines[i].split(":");
+                       //console.log("lks is: '%s', '%s'", stl[0], stl[1]);
+                       if(stl.length < 3) {
+                               console.log("Invalid config line '%s', should be 'appname:directory:appfilepath[:options]'", lines[i].trim());
+                       } else {
+                               var args = null;
+                               if(stl[3] != null) args = stl[3];
+                               nodes[stl[0]] = new Object();
+                               nodes[stl[0]].directory = stl[1];
+                               nodes[stl[0]].file = stl[2];
+                               nodes[stl[0]].args = args;
+                               console.log("Adding node config for '%s'", stl[0]);
+                       }
+               }
+       }
+       
+       // check our config against the current global
+       for(key in global.lxcnodejs) {
+               if(typeof nodes[key] == "undefined") {
+                       global.lxcnodejs[key].stop_and_kill = true;
+               } else {
+                       if(global.lxcnodejs[key].directory != nodes[key].directory) {
+                               if(debug_output) console.log("(debug) marking '%s' for restart on config change (directory)", key);
+                               global.lxcnodejs[key].directory = nodes[key].directory;
+                               global.lxcnodejs[key].restart = true;
+                       }
+                       if(global.lxcnodejs[key].file != nodes[key].file) {
+                               if(debug_output) console.log("(debug) marking '%s' for restart on config change (file)", key);
+                               global.lxcnodejs[key].file = nodes[key].file;
+                               global.lxcnodejs[key].restart = true;
+                       }
+                       if(global.lxcnodejs[key].args != nodes[key].args) {
+                               if(debug_output) console.log("(debug) marking '%s' for restart on config change (options)", key);
+                               global.lxcnodejs[key].args = nodes[key].args;
+                               global.lxcnodejs[key].restart = true;
+                       }
+               }
+       }
+       
+       // check for new configs and load them
+       for(key in nodes) {
+               if(typeof global.lxcnodejs[key] == "undefined") {
+                       if(debug_output) console.log("(debug) application '%s' is a new config, creating", key);
+                       global.lxcnodejs[key] = new Object();
+                       global.lxcnodejs[key].directory = nodes[key].directory;
+                       global.lxcnodejs[key].file = nodes[key].file;
+                       global.lxcnodejs[key].args = nodes[key].args;
+                       global.lxcnodejs[key].pid = -1;
+               }
+       }
+}
+
+function loadConfig(file) {
+       var configFile = fs.readFileSync(file).toString();
+       
+       var lines = configFile.split("\n");
+       
+       for(var i=0; i < lines.length; i++) {
+               //console.log("line: ", lines[i].trim());
+               var lks = lines[i].match(/^[^#][a-zA-Z0-9]+\=.*/);
+               if(lks!=null) {
+                       stl = lines[i].split("=");
+                       //console.log("lks is: '%s', '%s'", stl[0], stl[1]);
+                       if(stl.length !=2) {
+                               console.log("Invalid config line '%s', should be name=value format", lines[i].trim());
+                       } else {
+                               parseConfigLine(stl[0].trim(), stl[1].trim());
+                       }
+               }
+       }
+}
+
+function parseConfigLine(key, value) {
+       //console.log("parsing config '%s' = '%s'", key, value);
+       switch(key) {
+               case "ip4address":
+                       var lxon = value.split(":");
+                       var iface = "";
+                       var addrs = "";
+                       if(lxon.length!=2) {
+                               console.log("Address config isnt correct, expect 'interface:address/mask'");
+                       } else {
+                               iface = lxon[0];
+                               addrs = lxon[1];                                
+                               console.log("IPv4 address for '%s' is '%s'", iface, addrs);
+                               if(asroot) {
+                                               var nodespawn = require("child_process").spawn,
+                                                       nodeproc = nodespawn("/sbin/ip", ["link", "set", iface, "address", addrs, "up"], opts);                                 
+                               } else {
+                                       if(debug_output) console.log("(debug) would run /sbin/ip link set %s address %s up", iface, addrs);
+                               }
+                       }
+                       break;
+               case "ip6address":
+                       var lxon = value.split(":");
+                       var iface = "";
+                       var addrs = "";
+                       if(lxon.length!=2) {
+                               console.log("Address config isnt correct, expect 'interface:address/mask'");
+                       } else {
+                               iface = lxon[0];
+                               addrs = lxon[1];                                
+                               console.log("IPv4 address for '%s' is '%s'", iface, addrs);
+                       }
+                       break;
+               case "ip4gateway":
+                       console.log("Default IPv4 gateway is: '%s'", value);
+                       if(asroot) {
+                               var nodespawn = require("child_process").spawn,
+                               nodeproc = nodespawn("/sbin/ip", ["route", "add", "default", "via", value], opts);                                      
+                       } else {
+                               if(debug_output) console.log("(debug) would run /sbin/ip route add default via %s", value);
+                       }
+                       break;
+               case "checktimer":
+                       console.log("Setting health check timer to %s ms", value);
+                       default_loop_time = parseInt(value);
+                       if(default_loop_time < 100) {
+                               console.log("(warning) health check timer must be larger then 100, setting to 100, was %s", value);
+                               default_loop_time = 100;
+                       }
+                       break;
+               case "ip6gateway":
+                       console.log("Default IPv6 gateway is: '%s'", value);
+                       break;
+               case "dnsserver":
+                       console.log("DNS address set to: '%s'", value);
+                       break;
+               case "hostname":
+                       console.log("Hostname set to: '%s'", value);
+                       break;
+               case "debug":
+                       if(value == "true") {
+                               debug_output = true;
+                               console.log("(debug) Turning debugging on");
+                       }
+               default:
+                       console.log("Unknown config line: '%s' = '%s'", key, value);
+       }
+}
+
+function startApp(appname, directory, file, uid) {
+       
+    var opts = { uid: uid, env: process.env, cwd: directory };
+    var sleeplen = Math.floor(Math.random()*60);
+    var nodespawn = require("child_process").spawn;
+    var nodeproc = null;
+    
+    if(asroot) {
+        nodeproc = nodespawn("/node/bin/node", ["/node_code/nc.js"], opts);
+    } else {
+       if(debug_output) console.log("(debug) using sleep to emulator process '%s'", appname);
+       nodeproc = nodespawn("sleep", [sleeplen], opts);
+    }
+    
+    nodeproc.procname = appname;
+    global.lxcnodejs[appname].pid = nodeproc.pid;
+    
+    if(debug_output) console.log("(debug) application started with pid of %d", nodeproc.pid);
+    
+    nodeproc.stdout.on("data", function(data) {
+            console.log("data from nodespawn: '%s', '%s'", data, nodeproc.procname);
+    });
+    nodeproc.stderr.on("data", function(data) {
+            console.log("stderrdata from nodespawn: '%s', '%s'", data, nodeproc.procname);
+    });
+    nodeproc.on("exit", function(data) {
+            console.log("nodespawn died for '%s'", nodeproc.procname);
+            global.lxcnodejs[nodeproc.procname].pid = 0;
+    });
+}
+
+// main loop does all starting/restarting of applications
+function startMainLoop() {
+       // this loop kicks around every 5 seconds and just checks on the health of the processes or whatever
+       for(key in global.lxcnodejs) {
+               if(debug_output) console.log("(debug) checking state of '%s'", key);
+               if(typeof global.lxcnodejs[key].pid == "undefined") {
+                       // start it.
+                       console.log("Starting application '%s' (no pid was defined)", key);
+                       startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
+               } else if(global.lxcnodejs[key].pid == -1) {
+                       // start it.
+                       console.log("Starting application '%s' (initial start)", key);
+                       startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
+               } else if(global.lxcnodejs[key].pid == 0) {
+                       // start it.
+                       console.log("Starting application '%s' (pid 0, possible application crash?)", key);
+                       startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
+               } else if(typeof global.lxcnodejs[key].restart != "undefined") {
+                       // restart it
+                       console.log("Restarting application '%s' (restart defined)", key);
+                       startApp(key, global.lxcnodejs[key].directory, global.lxcnodejs[key].file, null);
+               } else if(typeof global.lxcnodejs[key].stop_and_kill != "undefined") {
+                       // kill it
+                       console.log("Restarting application '%s'", key);
+               } else {
+                       if(debug_output) console.log("(debug) state of '%s' is healthy", key);
+               }
+               
+               // we also need to check the control directory and see
+               // if theres any control files
+       }
+
+       // and restart the loop
+       setTimeout(startMainLoop, default_loop_time);
+}
\ No newline at end of file
diff --git a/lxc/lxc-nodejs b/lxc/lxc-nodejs
new file mode 100755 (executable)
index 0000000..df653ca
--- /dev/null
@@ -0,0 +1,274 @@
+#!/bin/bash
+
+#
+# lxc: linux Container library
+
+# Authors:
+# Paul Robinson <takigama@gmail.com>
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+setup_node()
+{
+       user=$3
+    rootfs=$1
+    name=$2
+    
+    echo "Creating default configuration, $rootfs/config - EDIT THIS CONFIG"
+    
+    pushd $rootfs
+    cat > config <<EOF
+# there arent many config variables here
+# just ip address, hostname and gateway
+# debug=true
+ip4address=eth0:10.0.3.100/24
+ip4address=lo:127.0.0.1/8
+ip6address=eth0:auto
+hostname=nd
+ip4gateway=10.0.3.1
+ip6gateway=auto
+dnsserver=10.0.3.1
+checktimer=1000
+EOF
+       popd
+
+    pushd $rootfs
+       cat > node_base/config <<EOF
+# this is where a list of apps to startup are stored
+# format is:
+# appname:directory:commandline[:opts]
+myapp:/node_code/somethingorother:dontrestart
+EOF
+       popd
+}
+
+
+install_node()
+{
+       user=$3
+    rootfs=$1
+    name=$2
+    res=0
+    tree="\
+$rootfs/dev \
+$rootfs/sbin \
+$rootfs/proc \
+$rootfs/etc \
+$rootfs/tmp \
+$rootfs/dev/pts \
+$rootfs/dev/shm \
+$rootfs/lib \
+$rootfs/usr/lib \
+$rootfs/node_modules \
+$rootfs/node_base \
+$rootfs/node_base/control \
+$rootfs/node_base/logs \
+$rootfs/lib64 \
+$rootfs/usr/lib64"
+
+       mkdir -p $tree || return 1
+    chmod 755 $tree || return 1
+       
+    pushd $rootfs/dev > /dev/null || return 1
+
+    # minimal devices needed for busybox
+    mknod tty c 5 0       || res=1
+    mknod console c 5 1   || res=1
+    chmod 666 tty console || res=1
+    mknod tty0 c 4 0      || res=1
+    mknod tty1 c 4 0      || res=1
+    mknod tty5 c 4 0      || res=1
+    chmod 666 tty0        || res=1
+    mknod ram0 b 1 0      || res=1
+    chmod 600 ram0        || res=1
+    mknod null c 1 3      || res=1
+    chmod 666 null        || res=1
+
+
+       echo "Downloading latest nodejs binaries for linux x64"
+       #fname=/tmp/nodejs.$RANDOM.`date +%s`tar.gz
+       # TODO: DONT FORGET TO REMOVE THIS
+       fname=/tmp/nodejs.11298.tar.gz
+       #wget -O $fname http://nodejs.org/dist/v0.8.17/node-v0.8.17-linux-x64.tar.gz
+       
+       pushd $rootfs
+       tar xfz $fname
+       ln -s node*linux*64* node
+       popd
+       
+       pushd $rootfs
+       echo "Installing ip cmd from upper OS"
+       cp /sbin/ip ./sbin/
+       popd
+       
+       pushd $rootfs
+       echo "PWD is $PWD"
+       cat >> sbin/init <<EOF
+#!/node/bin/node
+
+var uid=$user
+var fs = require("fs");
+var mnt = require("/node_modules/mount");
+
+mnt.mount("proc", "/proc", "proc");
+
+console.log("starting scheduler");
+setTimeout(scheduler, 2000);
+setTimeout(startnode, 4000);
+
+
+function scheduler()
+{
+        console.log("scheduler called: ", process.getuid());
+
+        //fs.readdir("/proc", function(err, files) {
+                //console.log("files: ", files);
+        //});
+
+        setTimeout(scheduler, 2000);
+}
+
+function startnode() {
+        var opts = { uid: uid, gid: uid };
+        var nodespawn = require("child_process").spawn,
+            nodeproc = nodespawn("/node/bin/node", ["/node_code/nc.js"], opts);
+        nodeproc.stdout.on("data", function(data) {
+                console.log("data from nodespawn: " + data);
+        });
+        nodeproc.stderr.on("data", function(data) {
+                console.log("stderrdata from nodespawn: " + data);
+        });
+        nodeproc.on("exit", function(data) {
+                console.log("nodespawn died");
+        });
+}
+EOF
+
+       chmod a+x sbin/init
+       popd
+       
+       pushd $rootfs
+       echo "Installing mount via npm"
+       ./node/bin/npm install mount
+       popd
+       
+       
+    return $res
+}
+
+
+copy_configuration()
+{
+    path=$1
+    rootfs=$2
+    name=$3
+
+cat <<EOF >> $path/config
+lxc.utsname = $name
+lxc.tty = 1
+lxc.pts = 1
+lxc.rootfs = $rootfs
+# uncomment the next line to run the container unconfined:
+#lxc.aa_profile = unconfined
+EOF
+
+if [ -d "$rootfs/lib" ]; then
+cat <<EOF >> $path/config
+lxc.mount.entry=/lib $rootfs/lib none ro,bind 0 0
+lxc.mount.entry=/usr/lib $rootfs/usr/lib none ro,bind 0 0
+EOF
+fi
+
+if [ -d "/lib64" ] && [ -d "$rootfs/lib64" ]; then
+cat <<EOF >> $path/config
+lxc.mount.entry=/lib64 $rootfs/lib64 none ro,bind 0 0
+EOF
+fi
+
+if [ -d "/usr/lib64" ] && [ -d "$rootfs/usr/lib64" ]; then
+cat <<EOF >> $path/config
+lxc.mount.entry=/usr/lib64 $rootfs/usr/lib64 none ro,bind 0 0
+EOF
+fi
+}
+
+usage()
+{
+    cat <<EOF
+$1 -h|--help -p|--path=<path> -u|--user=<uid> -i|--ip=<ip>
+EOF
+    return 0
+}
+
+user=0
+ip_last=$(($RANDOM%100+100))
+ip=10.0.3.$ip_last/24
+
+options=$(getopt -o hp:u:n:i:g: -l help,path:,user:,name:,ip:,gateway: -- "$@")
+if [ $? -ne 0 ]; then
+        usage $(basename $0)
+       exit 1
+fi
+eval set -- "$options"
+
+while true
+do
+    case "$1" in
+        -h|--help)      usage $0 && exit 0;;
+        -u|--user)             user=$2; shift 2;;
+        -i|--ip)               ip=$2; shift 2;;
+        -g|--gateway)          gw=$2; shift 2;;
+        -p|--path)      path=$2; shift 2;;
+       -n|--name)      name=$2; shift 2;;
+        --)             shift 1; break ;;
+        *)              break ;;
+    esac
+done
+
+if [ "$(id -u)" != "0" ]; then
+    echo "This script should be run as 'root'"
+    exit 1
+fi
+
+if [ -z "$path" ]; then
+    echo "'path' parameter is required"
+    exit 1
+fi
+
+rootfs=$path/rootfs
+
+install_node $rootfs $name $user
+if [ $? -ne 0 ]; then
+    echo "failed to install the nodejs rootfs"
+    exit 1
+fi
+
+setup_node $rootfs $name $user
+if [ $? -ne 0 ]; then
+       echo "Failed to setup node"
+       exit 1
+fi
+
+echo "sleeping for check - 20s"
+#sleep 20
+
+echo Params are, $user, $path, $name
+#exit 1
+
+copy_configuration $path $rootfs $name
+if [ $? -ne 0 ]; then
+    echo "failed to write configuration file"
+    exit 1
+fi
diff --git a/tftp/node_modules/.bin/tftp-client b/tftp/node_modules/.bin/tftp-client
new file mode 100755 (executable)
index 0000000..e48a173
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env node\r
+\r
+// Require needed modules\r
+var TFTP = require('..'), // The tftp-client module\r
+       fs = require('fs'), // Simple wrappers around standard POSIX functions\r
+       path = require('path'); // Contains utilities for handling and transforming file paths\r
+\r
+/**********************\\r
+|* Validate arguments *|\r
+\**********************/\r
+\r
+// Too few arguments (node + path_to_this_file + host + cmd + file = 5)\r
+if (process.argv.length < 5) {\r
+       // Print usage, and exit\r
+       console.log('Needs atleast 3 arguments (you had %d)', process.argv.length-2);\r
+       console.log('');\r
+       printUsage(true);\r
+}\r
+\r
+// Grab arguments\r
+var host = process.argv[2];\r
+var cmd  = process.argv[3];\r
+var file = process.argv[4];\r
+var port = process.argv[5] || 69; // Port defaults to 69 if omitted\r
+\r
+// If cmd is neither read nor write, print usage & exit\r
+if (['read', 'write'].indexOf(cmd)===-1) {\r
+       console.log('Command must be read or write (you had %s)', cmd);\r
+       printUsage(true);\r
+}\r
+\r
+/*******************\\r
+|* Validation done *|\r
+\*******************/\r
+\r
+// Create the client\r
+var client = new TFTP(port, host);\r
+\r
+var basename = path.basename(file);\r
+\r
+/********\\r
+|* READ *|\r
+\********/\r
+if (cmd == 'read') {\r
+       // Read from server\r
+       client.read(basename, function(err, data) {\r
+               // If error, output some error message\r
+               if (err) {\r
+                       console.error('Oh noes! Error while reading file from tftp server:');\r
+                       console.error(err);\r
+               } else {\r
+\r
+                       // No error, we got the file, lets write it\r
+                       \r
+                       fs.writeFile(file, data, function (err) {\r
+                               \r
+                               // If error, output some error message\r
+                               if (err) {\r
+                                       console.error('Dang it! Error while writing file!');\r
+                                       console.error(err);\r
+                               } else {\r
+\r
+                                       // No error, the file has been written!\r
+\r
+                                       console.log('File saved (%d bytes)', data.length);\r
+                               }\r
+                       });\r
+               }\r
+       });\r
+}\r
+/*********\\r
+|* WRITE *|\r
+\*********/\r
+else if (cmd == 'write') {\r
+       // If file does not exist, print error and usage & exit\r
+       if (!fs.existsSync(file)) {\r
+               console.log('File (%s) does not exist!', file);\r
+               printUsage(true);\r
+       }\r
+\r
+       // Read file first, then send to server\r
+       var data = fs.readFileSync(file);\r
+\r
+       client.write(basename, data, function (err, bytes) {\r
+               if (err) {\r
+                       console.error('ERROR:');\r
+                       console.error(err);\r
+                       return;\r
+               }\r
+\r
+               console.log('File sent (%d bytes)', bytes);\r
+       });\r
+} else {\r
+       console.log('???HOW DID YOU GET HERE???');\r
+}\r
+\r
+/**\r
+ * Prints usage - how to use tftp-client\r
+ * @param  {boolean} exit Wether it sould exit the process\r
+ */\r
+function printUsage(exit) {\r
+       console.log('Usage:');\r
+       console.log('  tftp-client <hostname> (read|write) <filename> [<port>]');\r
+       console.log('');\r
+       console.log('Example:');\r
+       console.log('  tftp-client localhost read 1.txt');\r
+\r
+       if (exit===true)\r
+               process.exit(0);\r
+}
\ No newline at end of file
diff --git a/tftp/node_modules/tftp-client/.npmignore b/tftp/node_modules/tftp-client/.npmignore
new file mode 100644 (file)
index 0000000..84a3249
--- /dev/null
@@ -0,0 +1,2 @@
+.git*\r
+example/
\ No newline at end of file
diff --git a/tftp/node_modules/tftp-client/README.md b/tftp/node_modules/tftp-client/README.md
new file mode 100644 (file)
index 0000000..ca09b89
--- /dev/null
@@ -0,0 +1,64 @@
+# TFTP-Client\r
+\r
+A simple TFTP client for Node.Js.  \r
+*Should not be used in production - first of all: this module is at an early stage (written in less than 12 hours), second: tftp is terrible*\r
+\r
+## Install\r
+\r
+### As a module\r
+`npm install tftp-client`\r
+\r
+### As CLI\r
+`npm install -g tftp-client`\r
+\r
+## Usage\r
+\r
+### Module\r
+\r
+`var client = new TFTP(port, client)` to create a new client.\r
+\r
+`client.read(filename, callback)` to **read** from the server.  \r
+ ~ The Callback is passed 2 arguments `(err, data)`, where `data` is the contents of the file.  \r
+\r
+`client.write(filename, data, callback)` to **write** to the server, where `data` is the contents of the file.  \r
+ ~ The callback is passed 2 arguments `(err, bytes)`, where `bytes` is the number of bytes sent.  \r
+\r
+**Simple read example:**\r
+\r
+```javascript\r
+var TFTP = require('tftp-client');\r
+\r
+// Initialize the tftp client\r
+var client = new TFTP(69, 'localhost');\r
+\r
+// Read 1.txt from the server\r
+client.read('1.txt', function (err, data) {\r
+       if (err) {\r
+               console.error('ERROR:');\r
+               console.error(err);\r
+               return;\r
+       }\r
+\r
+       console.log('Got data (%d bytes). First 100 bytes:', data.length);\r
+       console.log(data.toString('utf8', 0, 100));\r
+});\r
+```\r
+\r
+### Command line\r
+\r
+To install the tftp-client as CLI, run `npm install -g tftp-client`.\r
+\r
+`tftp-client <hostname> (read|write) <filename> [<port>]`\r
+* hostname - Hostname of tftp server\r
+* read|write - Wether you want to read or write\r
+* filename - Path to the file you want to read or write\r
+* port - Optional. Defaults to 69\r
+\r
+**Example**:\r
+`tftp-client localhost read 1.txt`\r
+\r
+## TODO\r
+\r
+- Error packets - [RFC](http://tools.ietf.org/html/rfc1350#page-8). (implemented, but not tested)\r
+- Do the initial connection as defined in section 4 (TID's: port numbers from request ack) - [RFC](http://tools.ietf.org/html/rfc1350#section-4).\r
+- Currently, any DATA or ACK packet is responded to. Eg. An ACK packet will get a `DATA` response. There is not check for the block numbers to be in order.\r
diff --git a/tftp/node_modules/tftp-client/TFTP-Client.sublime-project b/tftp/node_modules/tftp-client/TFTP-Client.sublime-project
new file mode 100644 (file)
index 0000000..02d0d4b
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "folders":
+       [
+               {
+                       "path": "/D/Dokumenter/GitHub/TFTP-Client"
+               }
+       ]
+}
diff --git a/tftp/node_modules/tftp-client/TFTP-Client.sublime-workspace b/tftp/node_modules/tftp-client/TFTP-Client.sublime-workspace
new file mode 100644 (file)
index 0000000..35cc7eb
--- /dev/null
@@ -0,0 +1,575 @@
+{
+       "auto_complete":
+       {
+               "selected_items":
+               [
+                       [
+                               "exists",
+                               "path.existsSync(path);"
+                       ],
+                       [
+                               "cons",
+                               "console.error();"
+                       ],
+                       [
+                               "D",
+                               "Dokumenter"
+                       ],
+                       [
+                               "process",
+                               "process.exit(code);"
+                       ],
+                       [
+                               "proces",
+                               "process.exit(code);"
+                       ],
+                       [
+                               "createse",
+                               "http.createServer(requestListener);"
+                       ],
+                       [
+                               "createSe",
+                               "http.createServer(requestListener);"
+                       ],
+                       [
+                               "child",
+                               "child_process.exec(command /*, options, callback */);"
+                       ],
+                       [
+                               "timeout",
+                               "timers.setTimeout(callback, after);"
+                       ],
+                       [
+                               "node",
+                               "node.events.EventEmitter.on    Snippet: NodeJS EventEmitter"
+                       ]
+               ]
+       },
+       "buffers":
+       [
+               {
+                       "file": "package.json",
+                       "settings":
+                       {
+                               "buffer_size": 242,
+                               "line_ending": "Windows",
+                               "name": "package.json"
+                       }
+               },
+               {
+                       "file": "lib/tftp.js",
+                       "settings":
+                       {
+                               "buffer_size": 7942,
+                               "line_ending": "Windows",
+                               "name": "tftp.js"
+                       }
+               },
+               {
+                       "file": "bin/tftp.js",
+                       "settings":
+                       {
+                               "buffer_size": 2683,
+                               "line_ending": "Windows"
+                       }
+               },
+               {
+                       "file": "README.md",
+                       "settings":
+                       {
+                               "buffer_size": 1790,
+                               "line_ending": "Windows",
+                               "name": "README.md"
+                       }
+               }
+       ],
+       "build_system": "",
+       "command_palette":
+       {
+               "height": 380.0,
+               "selected_items":
+               [
+                       [
+                               "mark",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "git diff",
+                               "Git: Diff Current File"
+                       ],
+                       [
+                               "markd",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "markdown",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "npm",
+                               "Nodejs::NPM::Command"
+                       ],
+                       [
+                               "npl",
+                               "Nodejs::NPM::List"
+                       ],
+                       [
+                               "install",
+                               "Package Control: Install Package"
+                       ],
+                       [
+                               "MARK",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "markdo",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "git",
+                               "Git: Gui"
+                       ],
+                       [
+                               "gitadd",
+                               "Git: Add..."
+                       ],
+                       [
+                               "remo",
+                               "Package Control: Remove Package"
+                       ],
+                       [
+                               "gut",
+                               "GitHub: Open Gist in Browser"
+                       ],
+                       [
+                               "intall",
+                               "Package Control: Install Package"
+                       ],
+                       [
+                               "snippet",
+                               "Snippet: setTimeout function"
+                       ],
+                       [
+                               "kar",
+                               "Package Control: Add Repository"
+                       ],
+                       [
+                               "ma",
+                               "Markdown Preview: preview in Sublime Text"
+                       ],
+                       [
+                               "list",
+                               "Package Control: List Packages"
+                       ],
+                       [
+                               "package",
+                               "Package Control: List Packages"
+                       ],
+                       [
+                               "mar",
+                               "Markdown Preview: preview in Browser"
+                       ],
+                       [
+                               "Package Control: list",
+                               "Package Control: List Packages"
+                       ],
+                       [
+                               "insta",
+                               "Package Control: Install Package"
+                       ],
+                       [
+                               "node",
+                               "Nodejs::Run::Current File"
+                       ],
+                       [
+                               "package up",
+                               "Package Control: Upgrade Package"
+                       ],
+                       [
+                               "list packa",
+                               "Package Control: Install Package"
+                       ],
+                       [
+                               "package install",
+                               "Package Control: Install Package"
+                       ],
+                       [
+                               "package discover",
+                               "Package Control: Discover Packages"
+                       ]
+               ],
+               "width": 512.0
+       },
+       "console":
+       {
+               "height": 227.0
+       },
+       "distraction_free":
+       {
+               "menu_visible": true,
+               "show_minimap": false,
+               "show_open_files": false,
+               "show_tabs": false,
+               "side_bar_visible": false,
+               "status_bar_visible": false
+       },
+       "file_history":
+       [
+               "/D/Dokumenter/GitHub/TFTP-Client/TFTP-Client.sublime-project",
+               "/D/Dokumenter/GitHub/TFTP-Client/.npmignore",
+               "/D/Dokumenter/GitHub/TFTP-Client/example/read.js",
+               "/D/Dokumenter/GitHub/TFTP-Client/example/write.js",
+               "/D/Dokumenter/GitHub/TFTP-Client/bin/tftp.js",
+               "/C/Users/Kristjan/AppData/Roaming/Sublime Text 2/Packages/Default/Default (Windows).sublime-keymap",
+               "/D/Dokumenter/GitHub/TFTP-Client/lib/write.js",
+               "/D/Dokumenter/GitHub/TFTP-Client/lib/read.js",
+               "/D/Dokumenter/GitHub/TFTP-Client/example/transaction.js",
+               "/D/Dokumenter/GitHub/JsControl/JsControl.sublime-project",
+               "/D/Dokumenter/GitHub/JsControl/test.js",
+               "/D/Dokumenter/GitHub/JsControl/.gitignore",
+               "/D/Dokumenter/GitHub/JsControl/JsControl.js",
+               "/D/Dokumenter/GitHub/JsControl/controller/test.js",
+               "/D/Dokumenter/GitHub/JsControl/package.json",
+               "/D/Dokumenter/GitHub/JsControl/plugins/playerlist/player.js",
+               "/D/Dokumenter/GitHub/JsControl/controller/modules/maps.js",
+               "/D/Dokumenter/GitHub/JsControl/controller/modules/game.js",
+               "/D/Dokumenter/GitHub/JsControl/config/config.json",
+               "/D/Dokumenter/GitHub/JsControl/Run.cmd",
+               "/D/Dokumenter/GitHub/JsControl/plugins/playerlist/index.js",
+               "/D/Dokumenter/GitHub/JsControl/config/players.sample.json",
+               "/D/Dokumenter/GitHub/JsControl/README.md",
+               "/D/Dokumenter/GitHub/JsControl/controller/modules/players/players.js",
+               "/D/Dokumenter/GitHub/JsControl/config/config.sample.json",
+               "/D/Dokumenter/GitHub/JsControl/todo.md",
+               "/D/Dokumenter/GitHub/JsControl/Plugins/second/index.js",
+               "/D/Dokumenter/GitHub/JsControl/controller/player.js",
+               "/D/Dokumenter/GitHub/JsControl/config/plugins/admin.sample.json",
+               "/D/Dokumenter/GitHub/JsControl/controller/players.js",
+               "/D/Dokumenter/GitHub/JsControl/Plugins/admin.js",
+               "/D/Dokumenter/GitHub/JsControl/plugins/commands.js",
+               "/D/Dokumenter/GitHub/JsControl/plugins/database.js",
+               "/C/Users/Kristjan/AppData/Roaming/Sublime Text 2/Packages/User/Distraction Free.sublime-settings",
+               "/C/Users/Kristjan/AppData/Roaming/Sublime Text 2/Packages/User/Default (Windows).sublime-keymap",
+               "/D/Dokumenter/GitHub/JsControl/Plugins/webcontroller/index.js",
+               "/D/Dokumenter/GitHub/JsControl/config/plugins/webcontroller.json",
+               "/D/Dokumenter/GitHub/JsControl/config/config.js",
+               "/D/Dokumenter/GitHub/JsControl/config/plugins/webcontroller.js",
+               "/D/Dokumenter/GitHub/node-gbxremote/package.json",
+               "/D/Dokumenter/GitHub/node-gbxremote/lib/gbxremote.js",
+               "/D/Dokumenter/GitHub/node-gbxremote/lib/client.js",
+               "/D/Dokumenter/GitHub/node-gbxremote/.gitignore",
+               "/D/Dokumenter/GitHub/node-gbxremote/example/client.js",
+               "/D/Dokumenter/GitHub/node-gbxremote/lib/deserializer.js",
+               "/D/Dokumenter/GitHub/node-gbxremote/example/authenticate.js",
+               "/C/Users/Kristjan/AppData/Roaming/Sublime Text 2/Packages/Default/Preferences.sublime-settings",
+               "/C/Users/Kristjan/AppData/Roaming/Sublime Text 2/Packages/User/Preferences.sublime-settings"
+       ],
+       "find":
+       {
+               "height": 34.0
+       },
+       "find_in_files":
+       {
+               "height": 0.0,
+               "where_history":
+               [
+                       "todo.md",
+                       "controller.js",
+                       ""
+               ]
+       },
+       "find_state":
+       {
+               "case_sensitive": false,
+               "find_history":
+               [
+                       "Timeout",
+                       "- ",
+                       "tftp is terrible",
+                       "argc",
+                       "length",
+                       "args",
+                       "line",
+                       "bytes",
+                       "createAck",
+                       "this.current",
+                       "netascii",
+                       "/",
+                       "   2         Access violation.\n   3         Disk full or allocation exceeded.\n   4         Illegal TFTP operation.\n   5         Unknown transfer ID.\n   6         File already exists.\n   7         No such user.\n",
+                       "tftp",
+                       "________________________________________________________",
+                       "client",
+                       "players",
+                       "PlayerList",
+                       "Maps",
+                       "this.query",
+                       "playerlist",
+                       "name",
+                       "this",
+                       "plugins",
+                       ".emit",
+                       ".on",
+                       "plugin",
+                       "requirePlugin",
+                       "require",
+                       "!!(",
+                       "(fl.pop())",
+                       "fl.pop()",
+                       "spec.pop()",
+                       "spec",
+                       "Is",
+                       "spectator",
+                       "self",
+                       "this",
+                       "console",
+                       "ingame",
+                       "shift",
+                       "plugin",
+                       "ready",
+                       "file",
+                       "Connection",
+                       "\\\\",
+                       "/",
+                       " /##################\\\\');",
+                       "function",
+                       "s",
+                       "loadPlugins",
+                       "               self.loadPlugins();\n",
+                       "loadPlugins",
+                       "               self.loadPlugins();\n           self.loadPlugins();\n           self.loadPlugins();\n           self.loadPlugins();\n           self.loadPlugins();\n",
+                       "               self.loadPlugins();\n           self.loadPlugins();\n           self.loadPlugins();\n           self.loadPlugins();\n",
+                       "               self.loadPlugins();\n",
+                       "loadPlugins",
+                       "function",
+                       "commands",
+                       "without",
+                       ".",
+                       "*",
+                       "ctrl+b",
+                       "Plugin",
+                       "password",
+                       "config.U",
+                       "config.I",
+                       "config.P",
+                       "\n\n   //return;\n     setTimeout(function () {\n              console.log(\"Exiting!\");\n            client.terminate();\n   }, 1000);",
+                       " [\"SuperAdmin\", \"SuperAdmin\"], function(err, result) {\n           if (err) {\n                    console.log(err);\n             } else {\n                      if (result === true)\n                          console.log(\"Authenticated!\");\n              }\n     });\n   \n      client.methodCall(\"EnableCallbacks\", [true], function(err, res) {\n           console.log(err, res);\n        });\n\n //return;\n     setTimeout(function () {\n              console.log(\"Exiting!\");\n            client.terminate();\n   }, 1000);\n}",
+                       "       });"
+               ],
+               "highlight": true,
+               "in_selection": false,
+               "preserve_case": false,
+               "regex": false,
+               "replace_history":
+               [
+                       "-",
+                       "config.u",
+                       "config.i",
+                       "config.p"
+               ],
+               "reverse": false,
+               "show_context": true,
+               "use_buffer2": true,
+               "whole_word": false,
+               "wrap": true
+       },
+       "groups":
+       [
+               {
+                       "selected": 3,
+                       "sheets":
+                       [
+                               {
+                                       "buffer": 0,
+                                       "file": "package.json",
+                                       "settings":
+                                       {
+                                               "buffer_size": 242,
+                                               "regions":
+                                               {
+                                               },
+                                               "selection":
+                                               [
+                                                       [
+                                                               242,
+                                                               242
+                                                       ]
+                                               ],
+                                               "settings":
+                                               {
+                                                       "auto_name": "package.json",
+                                                       "syntax": "Packages/JavaScript/JSON.tmLanguage"
+                                               },
+                                               "translation.x": 0.0,
+                                               "translation.y": 0.0,
+                                               "zoom_level": 1.0
+                                       },
+                                       "type": "text"
+                               },
+                               {
+                                       "buffer": 1,
+                                       "file": "lib/tftp.js",
+                                       "settings":
+                                       {
+                                               "buffer_size": 7942,
+                                               "regions":
+                                               {
+                                               },
+                                               "selection":
+                                               [
+                                                       [
+                                                               1556,
+                                                               1556
+                                                       ]
+                                               ],
+                                               "settings":
+                                               {
+                                                       "auto_name": "tftp.js",
+                                                       "syntax": "Packages/JavaScript/JavaScript.tmLanguage",
+                                                       "translate_tabs_to_spaces": false
+                                               },
+                                               "translation.x": 0.0,
+                                               "translation.y": 2077.0,
+                                               "zoom_level": 1.0
+                                       },
+                                       "type": "text"
+                               },
+                               {
+                                       "buffer": 2,
+                                       "file": "bin/tftp.js",
+                                       "settings":
+                                       {
+                                               "buffer_size": 2683,
+                                               "regions":
+                                               {
+                                               },
+                                               "selection":
+                                               [
+                                                       [
+                                                               2608,
+                                                               2640
+                                                       ]
+                                               ],
+                                               "settings":
+                                               {
+                                                       "syntax": "Packages/JavaScript/JavaScript.tmLanguage",
+                                                       "translate_tabs_to_spaces": false
+                                               },
+                                               "translation.x": 0.0,
+                                               "translation.y": 924.0,
+                                               "zoom_level": 1.0
+                                       },
+                                       "type": "text"
+                               },
+                               {
+                                       "buffer": 3,
+                                       "file": "README.md",
+                                       "settings":
+                                       {
+                                               "buffer_size": 1790,
+                                               "regions":
+                                               {
+                                               },
+                                               "selection":
+                                               [
+                                                       [
+                                                               1643,
+                                                               1643
+                                                       ]
+                                               ],
+                                               "settings":
+                                               {
+                                                       "auto_name": "README.md",
+                                                       "syntax": "Packages/Markdown/Markdown.tmLanguage"
+                                               },
+                                               "translation.x": 0.0,
+                                               "translation.y": 168.0,
+                                               "zoom_level": 1.0
+                                       },
+                                       "type": "text"
+                               }
+                       ]
+               }
+       ],
+       "incremental_find":
+       {
+               "height": 0.0
+       },
+       "input":
+       {
+               "height": 30.0
+       },
+       "layout":
+       {
+               "cells":
+               [
+                       [
+                               0,
+                               0,
+                               1,
+                               1
+                       ]
+               ],
+               "cols":
+               [
+                       0.0,
+                       1.0
+               ],
+               "rows":
+               [
+                       0.0,
+                       1.0
+               ]
+       },
+       "menu_visible": true,
+       "output.exec":
+       {
+               "height": 248.0
+       },
+       "output.git":
+       {
+               "height": 545.0
+       },
+       "replace":
+       {
+               "height": 0.0
+       },
+       "save_all_on_build": true,
+       "select_file":
+       {
+               "height": 0.0,
+               "selected_items":
+               [
+                       [
+                               "c",
+                               "JsControl.js"
+                       ],
+                       [
+                               "clien",
+                               "lib/client.js"
+                       ],
+                       [
+                               ".git",
+                               ".gitignore"
+                       ],
+                       [
+                               "packa",
+                               "package.json"
+                       ]
+               ],
+               "width": 0.0
+       },
+       "select_project":
+       {
+               "height": 500.0,
+               "selected_items":
+               [
+               ],
+               "width": 380.0
+       },
+       "show_minimap": true,
+       "show_open_files": false,
+       "show_tabs": true,
+       "side_bar_visible": true,
+       "side_bar_width": 212.0,
+       "status_bar_visible": true
+}
diff --git a/tftp/node_modules/tftp-client/bin/tftp.js b/tftp/node_modules/tftp-client/bin/tftp.js
new file mode 100755 (executable)
index 0000000..e48a173
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/env node\r
+\r
+// Require needed modules\r
+var TFTP = require('..'), // The tftp-client module\r
+       fs = require('fs'), // Simple wrappers around standard POSIX functions\r
+       path = require('path'); // Contains utilities for handling and transforming file paths\r
+\r
+/**********************\\r
+|* Validate arguments *|\r
+\**********************/\r
+\r
+// Too few arguments (node + path_to_this_file + host + cmd + file = 5)\r
+if (process.argv.length < 5) {\r
+       // Print usage, and exit\r
+       console.log('Needs atleast 3 arguments (you had %d)', process.argv.length-2);\r
+       console.log('');\r
+       printUsage(true);\r
+}\r
+\r
+// Grab arguments\r
+var host = process.argv[2];\r
+var cmd  = process.argv[3];\r
+var file = process.argv[4];\r
+var port = process.argv[5] || 69; // Port defaults to 69 if omitted\r
+\r
+// If cmd is neither read nor write, print usage & exit\r
+if (['read', 'write'].indexOf(cmd)===-1) {\r
+       console.log('Command must be read or write (you had %s)', cmd);\r
+       printUsage(true);\r
+}\r
+\r
+/*******************\\r
+|* Validation done *|\r
+\*******************/\r
+\r
+// Create the client\r
+var client = new TFTP(port, host);\r
+\r
+var basename = path.basename(file);\r
+\r
+/********\\r
+|* READ *|\r
+\********/\r
+if (cmd == 'read') {\r
+       // Read from server\r
+       client.read(basename, function(err, data) {\r
+               // If error, output some error message\r
+               if (err) {\r
+                       console.error('Oh noes! Error while reading file from tftp server:');\r
+                       console.error(err);\r
+               } else {\r
+\r
+                       // No error, we got the file, lets write it\r
+                       \r
+                       fs.writeFile(file, data, function (err) {\r
+                               \r
+                               // If error, output some error message\r
+                               if (err) {\r
+                                       console.error('Dang it! Error while writing file!');\r
+                                       console.error(err);\r
+                               } else {\r
+\r
+                                       // No error, the file has been written!\r
+\r
+                                       console.log('File saved (%d bytes)', data.length);\r
+                               }\r
+                       });\r
+               }\r
+       });\r
+}\r
+/*********\\r
+|* WRITE *|\r
+\*********/\r
+else if (cmd == 'write') {\r
+       // If file does not exist, print error and usage & exit\r
+       if (!fs.existsSync(file)) {\r
+               console.log('File (%s) does not exist!', file);\r
+               printUsage(true);\r
+       }\r
+\r
+       // Read file first, then send to server\r
+       var data = fs.readFileSync(file);\r
+\r
+       client.write(basename, data, function (err, bytes) {\r
+               if (err) {\r
+                       console.error('ERROR:');\r
+                       console.error(err);\r
+                       return;\r
+               }\r
+\r
+               console.log('File sent (%d bytes)', bytes);\r
+       });\r
+} else {\r
+       console.log('???HOW DID YOU GET HERE???');\r
+}\r
+\r
+/**\r
+ * Prints usage - how to use tftp-client\r
+ * @param  {boolean} exit Wether it sould exit the process\r
+ */\r
+function printUsage(exit) {\r
+       console.log('Usage:');\r
+       console.log('  tftp-client <hostname> (read|write) <filename> [<port>]');\r
+       console.log('');\r
+       console.log('Example:');\r
+       console.log('  tftp-client localhost read 1.txt');\r
+\r
+       if (exit===true)\r
+               process.exit(0);\r
+}
\ No newline at end of file
diff --git a/tftp/node_modules/tftp-client/lib/tftp.js b/tftp/node_modules/tftp-client/lib/tftp.js
new file mode 100644 (file)
index 0000000..4029627
--- /dev/null
@@ -0,0 +1,457 @@
+var dgram = require('dgram');  // UDP (datagram)\r
+\r
+// Timeout for ack/data packet\r
+var TIMEOUT = 5000; // 5 seconds\r
+// Max retransmits if timeout\r
+var MAX_RETRANSMITS = 3;\r
+\r
+// type: Opcode\r
+var types = {\r
+       'rrq': 1, // Read ReQuest\r
+       'wrq': 2, // Write ReQuest\r
+       'data':3,\r
+       'ack': 4,\r
+       'err': 5,\r
+\r
+       // aliases:\r
+       'read':1,\r
+       'write':2\r
+}\r
+\r
+// Error strings ([errorCode] = errorString)\r
+var errors = [\r
+       'Not defined, see error message (if any).',\r
+       'File not found.',\r
+       'Access violation.',\r
+       'Disk full or allocation exceeded.',\r
+       'Illegal TFTP operation.',\r
+       'Unknown transfer ID.',\r
+       'File already exists.',\r
+       'No such user.'\r
+];\r
+\r
+/**\r
+ * Constructor\r
+ * @param  {Number} port Port the tftp server is listening to\r
+ * @param  {String} ip   Ip or hostname of the tftp server\r
+ */\r
+var TFTP = module.exports = function(port, ip) {\r
+       this.ip = ip || '127.0.0.1';\r
+       this.port = port || 69;\r
+\r
+       // Hold the current operation\r
+       this.current = false;\r
+       // Holds the queue of the next operations\r
+       this.queue = [];\r
+\r
+       // Client will be created when needed\r
+       this.client = null;\r
+\r
+       // Display debug messages?\r
+       this.debug = false;\r
+}\r
+\r
+TFTP.prototype.createClient = function() {\r
+       if (this.client !== null)\r
+               return;\r
+\r
+       // We need this inside onMessage...\r
+       var self = this;\r
+       \r
+       // Called when getting an message from the server\r
+       var onMessage = function(msg, rinfo) {\r
+\r
+               // Type of message (see `types` above)\r
+               var type  = msg.readUInt16BE(0);\r
+\r
+               switch (type) {\r
+                       // Data - Respond with Ack\r
+                       case types.data:\r
+                               var block = msg.readUInt16BE(2);\r
+                               var data = msg.slice(4); // From byte 4 is data\r
+\r
+                               if (self.debug) {\r
+                                       console.log('> Received data. Block#: %d Bytes: %d', block, data.length);\r
+                                       console.log('< Sending ACK. Block#: %d', block);\r
+                               }\r
+\r
+                               // Concat the two buffers\r
+                               self.current.data = Buffer.concat([self.current.data, data]);\r
+\r
+                               // Create and send ACK packet\r
+                               var ack = createAck(block);\r
+                               self.send(ack);\r
+\r
+                               // If less than 512 bytes were received, it's the end...\r
+                               if (data.length < 512) {\r
+                                       // Send the data to the callback\r
+                                       self.current.callback(null, self.current.data);\r
+\r
+                                       // Go to next operation in queue\r
+                                       self.next();\r
+                               }\r
+                               break;\r
+                       // Ack - Respond with Data\r
+                       case types.ack:\r
+                               var block = msg.readUInt16BE(2);\r
+\r
+                               if (self.debug)\r
+                                       console.log('> Received ACK. Block#: %d', block);\r
+\r
+                               // If this is the ack for the last block\r
+                               if (block == self.current.blocks) {\r
+                                       // Send callback\r
+                                       self.current.callback(null, self.current.data.length);\r
+\r
+                                       // Go to next operation in queue\r
+                                       self.next();\r
+\r
+                                       // Break out of switch\r
+                                       break;\r
+                               }\r
+\r
+                               if (self.debug)\r
+                                       console.log('< Sending data. Block#: %d', block + 1);\r
+\r
+                               // Create the data packet for the next block of data\r
+                               var start = 512 * block;\r
+                               // End is `start + 512`, or end of data, whichever comes first\r
+                               var end   = Math.min(start + 512, self.current.data.length);\r
+                               var data = self.current.data.slice(start, end);\r
+\r
+                               var packet = createData(block+1, data);\r
+\r
+                               // Send data block\r
+                               self.send(packet);\r
+                               break;\r
+                       // Error\r
+                       case types.err:\r
+                               // Create new Error(), and send callback\r
+                               var errorCode = msg.readUInt16BE(2);\r
+                               var errMsg = msg.toString('ascii', 4, msg.length - 1);\r
+                               // @TODO: Take errMsg from `errors`, unless code == 0?\r
+                               var err = new Error(errMsg);\r
+                                       err.code = errorCode;\r
+\r
+                               if (self.debug)\r
+                                       console.log('> Received Error. code: %d - msg: %s', errorCode, ErrMsg);\r
+\r
+                               // Callback\r
+                               self.current.callback(err);\r
+\r
+                               // Go to next operation in queue\r
+                               self.next();\r
+                               break;\r
+               }\r
+       }\r
+\r
+       // Create socket, and listen to messages\r
+       this.client = dgram.createSocket("udp4", onMessage);\r
+};\r
+\r
+/**\r
+ * Shortcut for sending packet to server\r
+ * @param  {Buffer}   buff The buffer to send\r
+ * @param  {Function} cb   Callback - (err, bytes)\r
+ */\r
+TFTP.prototype.send = function(buff, cb) {\r
+       if (typeof cb !== 'function') cb = function() {};\r
+\r
+       // We need this later\r
+       var self = this;\r
+       \r
+       // Create function of sending this packet so that we can call it again on timeout\r
+       var send = function() {\r
+               self.client.send(buff, 0, buff.length, self.port, self.ip, cb);\r
+       };\r
+       // Send the packet already\r
+       send();\r
+\r
+       var onTimeout = function() {\r
+               // If we have retransmitted less than MAX_RETRANSMITS\r
+               if (MAX_RETRANSMITS > ++self.current.timeout.retransmits) {\r
+\r
+                       if (self.debug)\r
+                               console.log('! Timeout - Retransmitting (%d bytes)', buff.length);\r
+\r
+                       // Send the packet again\r
+                       send();\r
+\r
+                       // Set a new timeout for the packet we just sent\r
+                       self.current.timeout.id = setTimeout(onTimeout, TIMEOUT);\r
+               }\r
+\r
+               // If we sent too many retransmitts, the remote server did not respond...\r
+               else {\r
+                       if (self.debug)\r
+                               console.log('! Timeout - End');\r
+\r
+                       // Create error\r
+                       var err = new Error("Timeout");\r
+                       err.code = 0;\r
+\r
+                       self.current.callback(err);\r
+                       // Reset current, and check queue - (no answer from server)\r
+                       self.next();\r
+               }\r
+       }\r
+\r
+       // If there is a timeout, clear the timeout\r
+       if (this.current.timeout)\r
+               clearTimeout(this.current.timeout.id);\r
+\r
+       // Create a timeout object where we store information about this timeout\r
+       this.current.timeout = {\r
+               id: setTimeout(onTimeout, TIMEOUT),\r
+               retransmits: 0\r
+       }\r
+};\r
+\r
+/**\r
+ * Goes to next item in queue (if any). Same as `check()`, but clears current first\r
+ */\r
+TFTP.prototype.next = function() {\r
+       // Has to clear any timeout\r
+       if (this.current && this.current.timeout)\r
+               clearTimeout(this.current.timeout.id);\r
+\r
+       // Reset current, and check queue\r
+       this.current = false;\r
+       this.check();\r
+};\r
+\r
+/**\r
+ * Checks the Queue.\r
+ * If no operation is currently active, go to the next item in the queue (if any).\r
+ */\r
+TFTP.prototype.check = function() {\r
+       // If there currently is an active operation\r
+       // Or if the queue is empty, just return\r
+       if (this.current !== false)\r
+               return;\r
+\r
+       // If there is nothing running, and the queue is empty\r
+       if (this.queue.length == 0) {\r
+               // Close client\r
+               this.client.close();\r
+               return;\r
+       } else {\r
+               // Create client\r
+               this.createClient();\r
+       }\r
+\r
+       // Take the first item in queue\r
+       this.current = this.queue.shift();\r
+\r
+       // We need this later\r
+       var self = this;\r
+\r
+\r
+       if (self.debug)\r
+               console.log('< Sending request');\r
+\r
+       // Create the request...\r
+       var buff = createRequest(this.current.type, this.current.filename);\r
+       // Send the request\r
+       this.send(buff, function(err, bytes) {\r
+               // If there was an error sending the packet\r
+               if (err) {\r
+                       // Create Error message\r
+                       var err = new Error(['Error when sending ',\r
+                                                               self.current.type,\r
+                                                               ' request for ',\r
+                                                               self.current.filename].join());\r
+\r
+                       // Send error to the callback of the current operation\r
+                       self.current.callback(err);\r
+\r
+                       // Reset current\r
+                       self.current = false;\r
+\r
+                       // Then check queue\r
+                       self.check();\r
+               }\r
+       });\r
+};\r
+\r
+/**\r
+ * Sends a file to the server\r
+ * @param  {String}   filename File to send\r
+ * @param  {Buffer}   data     The data to write/send to the server\r
+ * @param  {Function} cb       Callback - (err)\r
+ */\r
+TFTP.prototype.write = function(filename, data, cb) {\r
+       if (typeof cb !== 'function') cb = function() {}; // Default cb to an empty function\r
+       if (!Buffer.isBuffer(data)) data = new Buffer(data); // If data is not a Buffer, make it a buffer\r
+\r
+       // Item to put into the queue\r
+       var queueItem = {\r
+               type: 'write',\r
+               filename: filename,\r
+               callback: cb,\r
+               data: data,\r
+               blocks: Math.ceil(data.length / 512) // Number of blocks to transfer data.\r
+                                                    // When we receive an ACK with this number, the transfer is finished\r
+       };\r
+\r
+       // Push the queueItem to the end of the queue.\r
+       this.queue.push(queueItem);\r
+\r
+       // Check the queue...\r
+       this.check();\r
+};\r
+\r
+/**\r
+ * Reads a file off the server\r
+ * @param  {String}   filename File to read from server\r
+ * @param  {Function} cb       Callback - (err, data)\r
+ */\r
+TFTP.prototype.read = function(filename, cb) {\r
+       if (typeof cb !== 'function') cb = function() {}; // Default cb to an empty function\r
+\r
+       // Item to put into the queue\r
+       var queueItem = {\r
+               type: 'read',\r
+               filename: filename,\r
+               callback: cb,\r
+               data: new Buffer(0) // Buffer of size 0 which will be filled up by onMessage\r
+       };\r
+\r
+       // Push the queueItem to the end of the queue.\r
+       this.queue.push(queueItem);\r
+\r
+       // Check the queue...\r
+       this.check();\r
+};\r
+\r
+/**\r
+ * Creates a buffer for a request.\r
+\r
+   2 bytes     string    1 byte     string   1 byte\r
+   ------------------------------------------------\r
+  | Opcode |  Filename  |   0  |    Mode    |   0  |\r
+   ------------------------------------------------\r
+\r
+ * @param  {String|Number} type     Int Opcode, or String (read|write|rrq|wrq)\r
+ * @param  {String} filename        Filename to add in the request\r
+ * @param  {String} mode            optional Mode (netascii|octet|email) - Defaults to octet\r
+ * @return {Buffer}                 The Buffer\r
+ */\r
+function createRequest(type, filename, mode) {\r
+       // Figure out the opcode for the type\r
+       if (typeof type === 'string') {\r
+               type = type.toLowerCase();\r
+\r
+               // If it exists in the types object, we found it\r
+               if (types.hasOwnProperty(type))\r
+                       type = types[type];\r
+               // If not, we dno what type\r
+               else\r
+                       throw 'Unknown type (' + type + ')';\r
+       }\r
+       // Not a string, nor a number, then we dno what type of request it is...\r
+       else if (typeof type !== 'number')\r
+               throw 'Unknown type (' + type + ')';\r
+\r
+       // Default mode to 'octet'\r
+       mode = mode || 'octet';\r
+\r
+       // Calculate the length of the buffer\r
+       // mode (2 byte opcode) + length of filename + 1 null byte + length of mode + 1 null byte.\r
+       var buffLen = 4 + filename.length + mode.length;\r
+\r
+       // Create the buffer\r
+       var buff = new Buffer(buffLen);\r
+       // Write mode (as unsigned 16 bit integer) on offset 0\r
+       buff.writeUInt16BE(type, 0);\r
+       // Write filename as ascii on offset 2\r
+       buff.write(filename, 2, 'ascii');\r
+       // Write mode as ascii on offset filename.length + 3 (type + filename null termination)\r
+       buff.write(mode, 2 + filename.length + 1, 'ascii');\r
+\r
+       // Make sure the ends of the strings are null\r
+       buff[2 + filename.length] = buff[buffLen - 1] = 0;\r
+\r
+       // Return the new buffer\r
+       return buff;\r
+}\r
+\r
+/**\r
+ * Creates a buffer for a data packet\r
+\r
+   2 bytes     2 bytes      n bytes\r
+   ----------------------------------\r
+  | Opcode |   Block #  |   Data     |\r
+   ----------------------------------\r
+\r
+ * @param  {Number} blockNumber Which block of the transaction it is\r
+ * @param  {String} data        The data to send\r
+ * @return {Buffer}             The Buffer\r
+ */\r
+function createData(blockNumber, data) {\r
+       var type = types['data'];\r
+\r
+       // Type + blocknumber + length of data(max 512)\r
+       var dataLen = Math.min(data.length, 512);\r
+       var buffLen = 4 + dataLen;\r
+\r
+       var buff = new Buffer(buffLen);\r
+\r
+       buff.writeUInt16BE(type, 0); // Type as UInt16BE on offset: 0\r
+       buff.writeUInt16BE(blockNumber, 2); // BlockNumber as UInt16BE on offset: 2\r
+       // Copy `data` into buff on offset 4. bytes 0 to 512 from `data`.\r
+       data.copy(buff, 4, 0, dataLen); // targetBuffer, targetStart, sourceStart, sourceEnd\r
+\r
+       return buff;\r
+}\r
+\r
+/**\r
+ * Creates a buffer for a ACK packet\r
+\r
+   2 bytes     2 bytes\r
+   ---------------------\r
+  | Opcode |   Block #  |\r
+   ---------------------\r
+\r
+ * @param  {Number} blockNumber Which block to ack\r
+ * @return {Buffer}             The Buffer\r
+ */\r
+function createAck(blockNumber) {\r
+       var type = types['ack'];\r
+\r
+       var buff = new Buffer(4);\r
+       buff.writeUInt16BE(type, 0); // Type as UInt16BE on offset: 0\r
+       buff.writeUInt16BE(blockNumber, 2); // BlockNumber as UInt16BE on offset: 2\r
+\r
+       return buff;\r
+}\r
+\r
+/**\r
+ * Creates a buffer for an ERROR packet\r
+\r
+   2 bytes     2 bytes      string    1 byte\r
+   -----------------------------------------\r
+  | Opcode |  ErrorCode |   ErrMsg   |   0  |\r
+   -----------------------------------------\r
+\r
+ * @param  {Number} code ErrorCode\r
+ * @param  {String} msg  Optional message - defaults to the message of the error code\r
+ * @return {Buffer}      The Buffer\r
+ */\r
+function createError(code, msg) {\r
+       // Default error message to the error message for the code (defined on top)\r
+       msg = msg || errors[code];\r
+       if (typeof msg !== 'string') msg = '';\r
+\r
+       var type = types['error'];\r
+\r
+       var buffLen = 4 + msg.length + 1;\r
+\r
+       var buff = new Buffer(buffLen);\r
+       buff.writeUInt16BE(type, 0); // Type as UInt16BE on offset: 0\r
+       buff.writeUInt16BE(code, 2); // ErrorCode as UInt16BE on offset: 2\r
+       buff.write(msg, 4, 'ascii'); // ErrMsg as ascii string on offset: 4\r
+       buff[buffLen - 1] = 0; // Last byte is 0\r
+\r
+       return buff;\r
+}\r
diff --git a/tftp/node_modules/tftp-client/package.json b/tftp/node_modules/tftp-client/package.json
new file mode 100644 (file)
index 0000000..f3a63be
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "name": "tftp-client",
+  "version": "0.2.1",
+  "description": "Very simple TFTP client",
+  "keywords": [
+    "tftp"
+  ],
+  "author": {
+    "name": "MiniGod"
+  },
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/MiniGod/TFTP-Client"
+  },
+  "main": "./lib/tftp.js",
+  "bin": {
+    "tftp-client": "./bin/tftp.js"
+  },
+  "readme": "# TFTP-Client\r\n\r\nA simple TFTP client for Node.Js.  \r\n*Should not be used in production - first of all: this module is at an early stage (written in less than 12 hours), second: tftp is terrible*\r\n\r\n## Install\r\n\r\n### As a module\r\n`npm install tftp-client`\r\n\r\n### As CLI\r\n`npm install -g tftp-client`\r\n\r\n## Usage\r\n\r\n### Module\r\n\r\n`var client = new TFTP(port, client)` to create a new client.\r\n\r\n`client.read(filename, callback)` to **read** from the server.  \r\n ~ The Callback is passed 2 arguments `(err, data)`, where `data` is the contents of the file.  \r\n\r\n`client.write(filename, data, callback)` to **write** to the server, where `data` is the contents of the file.  \r\n ~ The callback is passed 2 arguments `(err, bytes)`, where `bytes` is the number of bytes sent.  \r\n\r\n**Simple read example:**\r\n\r\n```javascript\r\nvar TFTP = require('tftp-client');\r\n\r\n// Initialize the tftp client\r\nvar client = new TFTP(69, 'localhost');\r\n\r\n// Read 1.txt from the server\r\nclient.read('1.txt', function (err, data) {\r\n\tif (err) {\r\n\t\tconsole.error('ERROR:');\r\n\t\tconsole.error(err);\r\n\t\treturn;\r\n\t}\r\n\r\n\tconsole.log('Got data (%d bytes). First 100 bytes:', data.length);\r\n\tconsole.log(data.toString('utf8', 0, 100));\r\n});\r\n```\r\n\r\n### Command line\r\n\r\nTo install the tftp-client as CLI, run `npm install -g tftp-client`.\r\n\r\n`tftp-client <hostname> (read|write) <filename> [<port>]`\r\n* hostname - Hostname of tftp server\r\n* read|write - Wether you want to read or write\r\n* filename - Path to the file you want to read or write\r\n* port - Optional. Defaults to 69\r\n\r\n**Example**:\r\n`tftp-client localhost read 1.txt`\r\n\r\n## TODO\r\n\r\n- Error packets - [RFC](http://tools.ietf.org/html/rfc1350#page-8). (implemented, but not tested)\r\n- Do the initial connection as defined in section 4 (TID's: port numbers from request ack) - [RFC](http://tools.ietf.org/html/rfc1350#section-4).\r\n- Currently, any DATA or ACK packet is responded to. Eg. An ACK packet will get a `DATA` response. There is not check for the block numbers to be in order.\r\n",
+  "readmeFilename": "README.md",
+  "_id": "tftp-client@0.2.1",
+  "dist": {
+    "shasum": "b66ceee832562b8a03ad616b40e3470f5dbd68c0"
+  },
+  "_from": "tftp-client"
+}
diff --git a/tftp/tftpd.js b/tftp/tftpd.js
new file mode 100644 (file)
index 0000000..c023096
--- /dev/null
@@ -0,0 +1,19 @@
+var dgram = require("dgram");
+var server = dgram.createSocket("udp4");
+
+
+exports.start = function(port) {
+       server.on("message", function (msg, rinfo) {
+               console.log("msg: ", msg);
+               console.log("server got: " + msg + " from " +
+               rinfo.address + ":" + rinfo.port);
+               parseMsg(msg);
+       });
+
+       server.on("listening", function () {
+               var address = server.address();
+               console.log("server listening " + address.address + ":" + address.port);
+       });
+
+       server.bind(port);
+}
\ No newline at end of file
diff --git a/tftp/tftpd_1.js b/tftp/tftpd_1.js
new file mode 100644 (file)
index 0000000..7d6c745
--- /dev/null
@@ -0,0 +1,55 @@
+var dgram = require("dgram");
+
+var server = dgram.createSocket("udp4");
+
+server.on("message", function (msg, rinfo) {
+       console.log("msg: ", msg);
+  console.log("server got: " + msg + " from " +
+    rinfo.address + ":" + rinfo.port);
+  parseMsg(msg);
+});
+
+server.on("listening", function () {
+  var address = server.address();
+  console.log("server listening " +
+      address.address + ":" + address.port);
+});
+
+function parseMsg(msg) {
+       var lkb = msg.readUInt16BE(0);
+       var fnamend = 0;
+       
+       for(i=2; i<(msg.length-1); i++) {
+               if(msg[i] == 0) {
+                       fnamend = i;
+                       console.log("setname end to ", fnamend);
+               }
+       }
+       
+       switch(lkb) {
+       case 1:
+               console.log("read request");
+               break;
+       case 2:
+               console.log("write request");
+               break;          
+       }
+       
+       var fname = msg.toString("utf8", 2, fnamend);
+       var ftype = msg.toString("utf8", fnamend+1, msg.length-1);
+       
+       console.log("fun is, '%s', '%s'", fname, ftype);
+       
+       switch(ftype) {
+       case "netascii":
+               console.log("was netascii");
+               break;
+       case "octet":
+               console.log("was octet");
+               break;
+       default:
+               console.log("errr: '", ftype, "'");
+       }
+}
+
+server.bind(41234);
\ No newline at end of file