--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+.git*\r
+example/
\ No newline at end of file
--- /dev/null
+# 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
--- /dev/null
+{
+ "folders":
+ [
+ {
+ "path": "/D/Dokumenter/GitHub/TFTP-Client"
+ }
+ ]
+}
--- /dev/null
+{
+ "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
+}
--- /dev/null
+#!/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
--- /dev/null
+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
--- /dev/null
+{
+ "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"
+}
--- /dev/null
+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
--- /dev/null
+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