├── .gitignore ├── README.md ├── lib ├── index.js ├── packet.js ├── packet │ ├── converters.js │ ├── options.js │ └── types.js ├── server.js ├── sprintf.js └── utils.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /tmp 2 | node_modules 3 | *.bkp 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **ARCHIVED** 2 | > activity on this library has stalled for quite some time and i won't update it any further. for other/up-to-date/better implementations have a look at [infusion/node-dhcp](https://github.com/infusion/node-dhcp) or [konobi/node-dhcpjs](https://github.com/konobi/node-dhcpjs) and [apaprocki/node-dhcpjs](https://github.com/apaprocki/node-dhcpjs). 3 | 4 | # Node.js DHCP server 5 | 6 | A DHCP (server) implementation purely written in JavaScript. 7 | 8 | ## Requirements 9 | 10 | node.js 0.8.x 11 | 12 | ## Installation 13 | 14 | Clone this into your `node_modules` folder. 15 | 16 | ## Usage 17 | 18 | ```js 19 | var dhcpd = require('node-dhcpd'); 20 | var server = new dhcpd('udp4', { broadcast: '192.168.0.255' }); 21 | ``` 22 | 23 | For a real-world example have a look at [@konobi's forge](https://github.com/konobi/forge/). 24 | 25 | ## Contributing 26 | 27 | 1. Fork it 28 | 2. Create your feature branch (`git checkout -b my-new-feature`) 29 | 3. Commit your changes (`git commit -am 'Added some feature'`) 30 | 4. Push to the branch (`git push origin my-new-feature`) 31 | 5. Create new Pull Request 32 | 33 | # License 34 | 35 | Published under the [MIT license](http://opensource.org/licenses/MIT). 36 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | module.exports = { 4 | Packet: require('./packet'), 5 | MessageTypes: require('./packet/types'), 6 | Options: require('./packet/options') 7 | }; 8 | -------------------------------------------------------------------------------- /lib/packet.js: -------------------------------------------------------------------------------- 1 | 2 | var utils = require('./utils'); 3 | 4 | var get_convert = require('./packet/converters'); 5 | 6 | function Packet(array) { 7 | var key; 8 | for (key in array) { 9 | if(array.hasOwnProperty(key)){ 10 | this[key] = array[key]; 11 | } 12 | } 13 | } 14 | 15 | function stripBinNull(str) { 16 | var pos; 17 | pos = str.indexOf('\u0000'); 18 | if (pos === -1) { 19 | return str; 20 | } else { 21 | return str.substr(0, pos); 22 | } 23 | } 24 | 25 | var fromBuffer = function(b) { 26 | var i, optLen, optNum, optVal, options, ret, _ref; 27 | ret = { 28 | op: b[0], 29 | htype: b[1], 30 | hlen: b.readUInt8(2), 31 | hops: b.readUInt8(3), 32 | xid: b.readUInt32BE(4), 33 | secs: b.readUInt16BE(8), 34 | flags: b.readUInt16BE(10), 35 | ciaddr: utils.readIp(b, 12), 36 | yiaddr: utils.readIp(b, 16), 37 | siaddr: utils.readIp(b, 20), 38 | giaddr: utils.readIp(b, 24), 39 | chaddr: utils.readMacAddress(b.slice(28, 28 + b.readUInt8(2))), 40 | sname: stripBinNull(b.toString('ascii', 44, 108)), 41 | file: stripBinNull(b.toString('ascii', 108, 236)), 42 | options: {} 43 | }; 44 | _ref = [0, b.slice(240)]; i = _ref[0]; options = _ref[1]; 45 | while (i < options.length && options[i] !== 255) { 46 | optNum = parseInt(options[i++], 10); 47 | optLen = parseInt(options[i++], 10); 48 | var converter = get_convert(optNum); 49 | optVal = converter.decode(options.slice(i, i + optLen), optNum); 50 | ret.options[optNum] = optVal; 51 | i += optLen; 52 | } 53 | return new Packet(ret); 54 | }; 55 | 56 | var toBuffer = function() { 57 | var buffer, hex, i, key, octet, opt, padded, pos, value, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3; 58 | buffer = new Buffer(512, 'ascii'); 59 | buffer[0] = this.op; 60 | buffer[1] = this.htype; 61 | buffer.writeUInt8(this.hlen, 2); 62 | buffer.writeUInt8(this.hops, 3); 63 | buffer.writeUInt32BE(this.xid, 4); 64 | buffer.writeUInt16BE(this.secs, 8); 65 | buffer.writeUInt16BE(this.flags, 10); 66 | pos = 12; 67 | _ref = ["ciaddr", "yiaddr", "siaddr", "giaddr"]; 68 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 69 | key = _ref[_i]; 70 | _ref1 = (this[key] || "0.0.0.0").split("."); 71 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 72 | octet = _ref1[_j]; 73 | buffer.writeUInt8(parseInt(octet, 10), pos++); 74 | } 75 | } 76 | _ref2 = this.chaddr.split(':'); 77 | for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { 78 | hex = _ref2[_k]; 79 | buffer[pos++] = parseInt(hex, 16); 80 | } 81 | buffer.fill(0, 43, 235); 82 | buffer.write(this.sname, 43, 64, 'ascii'); 83 | buffer.write(this.fname, 109, 128, 'ascii'); 84 | pos = 236; 85 | _ref3 = [99, 130, 83, 99]; 86 | for (_l = 0, _len3 = _ref3.length; _l < _len3; _l++) { 87 | i = _ref3[_l]; 88 | buffer[pos++] = i; 89 | } 90 | pos = 240; 91 | for (opt in this.options) { 92 | if(this.options.hasOwnProperty(opt)){ 93 | value = this.options[opt]; 94 | var converter = get_convert(opt); 95 | pos = converter.encode(buffer, opt, value, pos); 96 | } 97 | } 98 | buffer[pos] = 255; 99 | padded = new Buffer(pos, 'ascii'); 100 | buffer.copy(padded, 0, 0, pos); 101 | return padded; 102 | }; 103 | 104 | Packet.fromBuffer = fromBuffer; 105 | 106 | Packet.prototype.toBuffer = toBuffer; 107 | 108 | Packet.prototype.getRequestedIPAddress = function() { 109 | return this.options[50]; 110 | }; 111 | 112 | Packet.prototype.op = function(op) { 113 | this.op = op; 114 | return this; 115 | }; 116 | 117 | Packet.prototype.htype = function(htype) { 118 | this.htype = htype; 119 | return this; 120 | }; 121 | 122 | Packet.prototype.hlen = function(hlen) { 123 | this.hlen = hlen; 124 | return this; 125 | }; 126 | 127 | Packet.prototype.hops = function(hops) { 128 | this.hops = hops; 129 | return this; 130 | }; 131 | 132 | Packet.prototype.xid = function(xid) { 133 | this.xid = xid; 134 | return this; 135 | }; 136 | 137 | Packet.prototype.secs = function(secs) { 138 | this.secs = secs; 139 | return this; 140 | }; 141 | 142 | Packet.prototype.flags = function(flags) { 143 | this.flags = flags; 144 | return this; 145 | }; 146 | 147 | Packet.prototype.ciaddr = function(ciaddr) { 148 | this.ciaddr = ciaddr !== null ? ciaddr : '0.0.0.0'; 149 | return this; 150 | }; 151 | 152 | Packet.prototype.yiaddr = function(yiaddr) { 153 | this.yiaddr = yiaddr !== null ? yiaddr : '0.0.0.0'; 154 | return this; 155 | }; 156 | 157 | Packet.prototype.siaddr = function(siaddr) { 158 | this.siaddr = siaddr !== null ? siaddr : '0.0.0.0'; 159 | return this; 160 | }; 161 | 162 | Packet.prototype.giaddr = function(giaddr) { 163 | this.giaddr = giaddr !== null ? giaddr : '0.0.0.0'; 164 | return this; 165 | }; 166 | 167 | Packet.prototype.chaddr = function(chaddr) { 168 | this.chaddr = chaddr; 169 | return this; 170 | }; 171 | 172 | Packet.prototype.sname = function(sname) { 173 | this.sname = sname; 174 | return this; 175 | }; 176 | 177 | Packet.prototype.file = function(file) { 178 | this.file = file; 179 | return this; 180 | }; 181 | 182 | Packet.prototype.options = function(options) { 183 | this.options = options; 184 | return this; 185 | }; 186 | 187 | module.exports = { 188 | Packet: Packet, 189 | fromBuffer: fromBuffer, 190 | toBuffer: toBuffer 191 | }; 192 | -------------------------------------------------------------------------------- /lib/packet/converters.js: -------------------------------------------------------------------------------- 1 | /* 2 | Static class to manage the collection 3 | of converters (encoder/decoder) 4 | found in 'packet/converters/' 5 | 6 | The object signature for each converter must be: 7 | 8 | { 9 | encode: function(buf, num, value, offset) { return offset; }, 10 | decode: function(buf) { return null; } 11 | } 12 | */ 13 | var utils = require('../utils'); 14 | var sprintf = require('../sprintf'); 15 | 16 | var converters = { 17 | // option 1: subnet mask 18 | 1: { 19 | encode: utils.writeIp, 20 | decode: utils.readIp 21 | }, 22 | 23 | // option 2: time offset 24 | 2: { 25 | encode: function encode(buf, num, value, offset) { 26 | buf[offset++] = num; 27 | buf[offset++] = 4; 28 | utils.writeInt32(buf, value, offset); 29 | return offset + 4; 30 | }, 31 | decode:function decode(buf) { 32 | return utils.readInt32(buf, 0); 33 | } 34 | }, 35 | 36 | // option 3: routers 37 | 3: { 38 | decode: function decode(buf) { 39 | var i, numRecords, pos, records; 40 | numRecords = buf.length / 4; 41 | pos = 0; 42 | records = []; 43 | i = 0; 44 | while (i < numRecords) { 45 | records.push(sprintf("%d.%d.%d.%d", buf[pos++], buf[pos++], buf[pos++], buf[pos++])); 46 | i++; 47 | } 48 | return records; 49 | }, 50 | 51 | encode: function encode(buf, num, data, offset) { 52 | var routers; 53 | routers = data; 54 | buf[offset++] = num; 55 | buf[offset++] = routers.length * 4; 56 | routers.forEach(function(ip) { 57 | return ip.split(".").forEach(function(item) { 58 | buf[offset++] = item; 59 | }); 60 | }); 61 | return offset; 62 | } 63 | }, 64 | 65 | // option 6: dns servers 66 | 6: { 67 | decode: function(buf) { 68 | var i, numRecords, pos, records; 69 | numRecords = buf.length / 4; 70 | pos = 0; 71 | records = []; 72 | i = 0; 73 | while (i < numRecords) { 74 | records.push(sprintf("%d.%d.%d.%d", buf[pos++], buf[pos++], buf[pos++], buf[pos++])); 75 | i++; 76 | } 77 | return records; 78 | }, 79 | 80 | encode: function(buf, num, data, offset) { 81 | var routers; 82 | routers = data.split(","); 83 | buf[offset++] = num; 84 | buf[offset++] = routers.length * 4; 85 | routers.forEach(function(ip) { 86 | return ip.split(".").forEach(function(item) { 87 | buf[offset++] = item; 88 | }); 89 | }); 90 | return offset; 91 | } 92 | }, 93 | 94 | /* 95 | // option 119: dns search list 96 | 119: { 97 | encode: null, 98 | decode: null 99 | }, 100 | */ 101 | 102 | // option 12: hostname 103 | 12: { 104 | encode: utils.writeString, 105 | decode: utils.readString 106 | }, 107 | 108 | // option 15: dns domain name 109 | 15: { 110 | encode: utils.writeString, 111 | decode: utils.readString 112 | }, 113 | 114 | // option 28: broadcast address 115 | 28: { 116 | encode: utils.writeIp, 117 | decode: utils.readIp 118 | }, 119 | 120 | // option 31: router discovery 121 | 31: { 122 | encode: function(buf, num, value, offset) { 123 | buf[offset++] = num; 124 | buf[offset++] = 1; 125 | utils.writeInt8(buf, value, offset); 126 | return offset + 1; 127 | }, 128 | 129 | decode: function(buf) { 130 | return utils.readInt8(buf, 0); 131 | } 132 | }, 133 | 134 | // option 33: static routes 135 | 33: { 136 | decode: function decode(buf) { 137 | var i, numRecords, pos, records; 138 | numRecords = buf.length / 4; 139 | pos = 0; 140 | records = []; 141 | i = 0; 142 | while (i < numRecords) { 143 | records.push(sprintf("%d.%d.%d.%d", buf[pos++], buf[pos++], buf[pos++], buf[pos++])); 144 | i++; 145 | } 146 | return records; 147 | }, 148 | 149 | encode: function encode(buf, num, data, offset) { 150 | var routers; 151 | routers = data.split(","); 152 | buf[offset++] = num; 153 | buf[offset++] = routers.length * 4; 154 | routers.forEach(function(ip) { 155 | return ip.split(".").forEach(function(item) { 156 | buf[offset++] = item; 157 | }); 158 | }); 159 | return offset; 160 | } 161 | }, 162 | 163 | // option 44: netbios name servers 164 | 44: { 165 | decode: function(buf) { 166 | var i, numRecords, pos, records; 167 | numRecords = buf.length / 4; 168 | pos = 0; 169 | records = []; 170 | i = 0; 171 | while (i < numRecords) { 172 | records.push(sprintf("%d.%d.%d.%d", buf[pos++], buf[pos++], buf[pos++], buf[pos++])); 173 | i++; 174 | } 175 | return records; 176 | }, 177 | 178 | encode: function(buf, num, data, offset) { 179 | var routers; 180 | routers = data.split(","); 181 | buf[offset++] = num; 182 | buf[offset++] = routers.length * 4; 183 | routers.forEach(function(ip) { 184 | return ip.split(".").forEach(function(item) { 185 | buf[offset++] = item; 186 | }); 187 | }); 188 | return offset; 189 | } 190 | }, 191 | 192 | // option 46: netbios node type 193 | 46: { 194 | decode: function(buf) { 195 | return parseInt(buf[0], 16); 196 | }, 197 | encode: function(buf, num, value, offset) { 198 | buf[offset++] = num; 199 | buf[offset++] = 1; 200 | buf[offset++] = parseInt(buf[0], 10).toString(16); 201 | return offset; 202 | } 203 | }, 204 | 205 | // option 47: netbios node type 206 | 47: { 207 | encode: utils.writeString, 208 | decode: utils.readString 209 | }, 210 | 211 | // option 50: requested ip address 212 | 50: { 213 | encode: utils.writeIp, 214 | decode: utils.readIp 215 | }, 216 | 217 | // option 51: lease time 218 | 51: { 219 | decode: function(buf) { 220 | return utils.readInt32(buf, 0); 221 | }, 222 | 223 | encode: function(buf, num, value, offset) { 224 | buf[offset++] = num; 225 | buf[offset++] = 4; 226 | utils.writeInt32(buf, value, offset); 227 | return offset + 4; 228 | } 229 | }, 230 | 231 | // option 53: message type 232 | 53: { 233 | decode: function(buf) { 234 | return parseInt(buf[0], 10); 235 | }, 236 | encode: function(buf, num, value, offset) { 237 | buf[offset++] = 53; 238 | buf[offset++] = 1; 239 | buf[offset++] = value; 240 | return offset; 241 | } 242 | }, 243 | 244 | // option 54: server identifier 245 | 54: { 246 | encode: utils.writeIp, 247 | decode: function decode(buf) { 248 | return utils.readIp(buf, 0); 249 | } 250 | }, 251 | 252 | // option 55: parameter request list 253 | 55: { 254 | decode: function decode(buf) { 255 | var records = []; 256 | var i = 0; 257 | var len = buf.length; 258 | while (i < len) { 259 | records[i] = buf[i]; 260 | i++; 261 | } 262 | return records; 263 | }, 264 | 265 | encode: function encode(buf, num, data, offset) { 266 | buf[offset++] = num; 267 | buf[offset++] = data.length; 268 | var i = 0; 269 | while(i < data.length) { 270 | utils.writeInt8(buf, data[i], offset); 271 | i++; 272 | offset++; 273 | } 274 | return offset; 275 | } 276 | }, 277 | 278 | // option 57: max message size 279 | 57: { 280 | encode: function(buf, num, value, offset) { 281 | buf[offset++] = 57; 282 | buf[offset++] = 2; 283 | utils.writeInt16(buf, value, offset); 284 | return offset + 2; 285 | }, 286 | 287 | decode: function(buf) { 288 | return utils.readInt16(buf, 0); 289 | } 290 | }, 291 | 292 | // option 61: client identifier 293 | 61: { 294 | encode: function(buf, num, value, offset) { 295 | return offset; 296 | }, 297 | 298 | decode: function(buf) { 299 | var j, s, type; 300 | s = []; 301 | type = buf[0]; 302 | j = 1; 303 | while (j < buf.length) { 304 | s.push(sprintf("%02d", buf[j])); 305 | j++; 306 | } 307 | return [type, s.join(":")]; 308 | } 309 | }, 310 | 311 | // option 67: bootfile name 312 | 67: { 313 | encode: utils.writeString, 314 | decode: utils.readString 315 | }, 316 | 317 | // option 77: user class information 318 | 77: { 319 | encode: utils.writeString, 320 | decode: function(buf) { 321 | var records = []; 322 | offset = 0; 323 | while(buf[offset]){ 324 | var uc_len = buf[offset]; 325 | var uc_data = buf.slice(offset++, uc_len); 326 | offset += uc_len; 327 | records.push(uc_data.toString('ascii')); 328 | } 329 | return records.join(':'); 330 | } 331 | }, 332 | 333 | // option 83: client fqdn 334 | 83: { 335 | encode: function(buf, num, value, offset) { 336 | return offset; 337 | }, 338 | 339 | decode: function(buf) { 340 | var ret; 341 | ret = ""; 342 | ret += sprintf("%d", buf[0]) + "-"; 343 | ret += sprintf("%d", buf[1]) + "-"; 344 | ret += sprintf("%d", buf[2]) + " "; 345 | ret += utils.toString(buf.slice(3)); 346 | return ret; 347 | } 348 | }, 349 | 350 | // END OF THE LINE... SUCKA 351 | 255: { 352 | encode: function(buf, num, value, offset){ 353 | utils.writeInt8(buf, 255, offset); 354 | return offset + 1; 355 | }, 356 | decode: function(buf) { 357 | return undefined; 358 | } 359 | } 360 | }; 361 | 362 | var stub = { 363 | encode: function(buf, num, value, offset) { 364 | //console.error("[node-dhcpd] encoder for option " + num + " not found"); 365 | return offset; 366 | }, 367 | decode: function(buf, num) { 368 | //console.error("[node-dhcpd] decoder for option " + num + " not found"); 369 | //console.log(" buffer:", buf); 370 | return null; 371 | } 372 | }; 373 | 374 | module.exports = function(i) { 375 | //console.log("GET CONVERTER FOR " + i); 376 | return (i in converters) ? converters[i] : stub; 377 | }; 378 | 379 | -------------------------------------------------------------------------------- /lib/packet/options.js: -------------------------------------------------------------------------------- 1 | module.exports = ["pad", "Subnet mask", "Time offset", "Routers", "Time server", "Name server", "DNS server", "Log server", "Cookie server", "LPR server", "Impress server", "Resource location server", "Host name", "Boot file size", "Merit dump file", "Domainname", "Swap server", "Root path", "Extensions path", "IP forwarding", "Non-local source routing", "Policy filter", "Maximum datagram reassembly size", "Default IP TTL", "Path MTU aging timeout", "Path MTU plateau table", "Interface MTU", "All subnets local", "Broadcast address", "Perform mask discovery", "Mask supplier", "Perform router discovery", "Router solicitation", "Static route", "Trailer encapsulation", "ARP cache timeout", "Ethernet encapsulation", "TCP default TTL", "TCP keepalive interval", "TCP keepalive garbage", "NIS domain", "NIS servers", "NTP servers", "Vendor specific info", "NetBIOS name server", "NetBIOS datagram distribution server", "NetBIOS node type", "NetBIOS scope", "X Window System font server", "X Window System display server", "Request IP address", "IP address leasetime", "Option overload", "DHCP message type", "Server identifier", "Parameter Request List", "Message", "Maximum DHCP message size", "T1", "T2", "Vendor class identifier", "Client-identifier", "Netware/IP domain name", "Netware/IP domain information", "NIS+ domain", "NIS+ servers", "TFTP server name", "Bootfile name", "Mobile IP home agent", "SMTP server", "POP3 server", "NNTP server", "WWW server", "Finger server", "IRC server", "StreetTalk server", "StreetTalk directory assistance server", "User-class Identification", "SLP-directory-agent", "SLP-service-scope", "Naming Authority", "Client FQDN", "Relay Agent Information", "Agent Remote ID", "Agent Subnet Mask", "NDS server", "NDS tree name", "NDS context", "IEEE 1003.1 POSIX", "FQDN", "Authentication", "Vines TCP/IP", "Server Selection", "Client System", "Client NDI", "LDAP", "IPv6 Transitions", "UUID/GUID", "UPA servers", "???", "Printer Name", "MDHCP", "???", "???", "???", "???", "???", "???", "Swap Path", "???", "IPX Compatability", "???", "Netinfo Address", "Netinfo Tag", "URL", "DHCP Failover", "DHCP Autoconfiguration", "Name Service Search", "Subnet selection", "Domain Search", "SIP Servers DHCP Option", "Classless Static Route", "???", "???", "???", "???", "Extension", "Extension", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "HP - TFTP file", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "Authenticate", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "???", "MSFT - Classless route", "???", "???", "MSFT - WinSock Proxy Auto Detect", "???", "???", "End"]; 2 | -------------------------------------------------------------------------------- /lib/packet/types.js: -------------------------------------------------------------------------------- 1 | module.exports = ["unknown dhcp message type", "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE", "DHCPACK", "DHCPNAK", "DHCPRELEASE", "DHCPINFORM"]; 2 | -------------------------------------------------------------------------------- /lib/server.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var dhcp = require('./index'); 3 | var Socket = require('dgram').Socket; 4 | 5 | 6 | function DHCPServer(type, opts) { 7 | var _this = this; 8 | DHCPServer.super_.apply(this, [type]); 9 | _this.broadcast = opts.broadcast; 10 | this.on('error', function(err){ 11 | console.dir(err); 12 | }); 13 | this.on('message', function(buffer, remote) { 14 | var event_name, packet, type; 15 | packet = dhcp.Packet.fromBuffer(buffer); 16 | if (packet.op === 1) { 17 | type = { 18 | id: packet.options[53] || 0, 19 | name: dhcp.MessageTypes[packet.options[53] || 0] 20 | }; 21 | //util.log(("Got " + type.name + " from") + (" " + remote.address + ":" + remote.port + " (" + packet.chaddr + ") ") + (" with packet length of " + buffer.length + " bytes")); 22 | event_name = type.name.replace('DHCP', '').toLowerCase(); 23 | return _this._emitPacket(event_name, packet); 24 | } else { 25 | return console.log(" Unsupported message format"); 26 | } 27 | }); 28 | return _this; 29 | } 30 | util.inherits(DHCPServer, Socket); 31 | 32 | DHCPServer.prototype.bind = function(port, addr, cb) { 33 | var self = this; 34 | var res; 35 | res = DHCPServer.super_.prototype.bind.call(this, port, addr, function(){ 36 | this.setBroadcast(true); 37 | if(cb instanceof Function) cb(); 38 | }); 39 | return res; 40 | }; 41 | 42 | DHCPServer.prototype._send = function(event_name, ip, packet) { 43 | var buffer, 44 | _this = this; 45 | buffer = packet.toBuffer(); 46 | //util.log(("Sending " + dhcp.MessageTypes[packet.options[53]]) + (" to " + ip + ":68 (" + packet.chaddr + ")") + (" with packet length of " + buffer.length + " bytes")); 47 | this.emit(event_name, packet); 48 | return this.send(buffer, 0, buffer.length, 68, ip, function(err, bytes) { 49 | if (err) { 50 | return _this.emit("" + event_name + "Error", err, packet); 51 | } else { 52 | return _this.emit("" + event_name + "Sent", bytes, packet); 53 | } 54 | }); 55 | }; 56 | 57 | DHCPServer.prototype._emitPacket = function(message_type, packet) { 58 | return this.emit(message_type, packet, packet.options[50] || null); 59 | }; 60 | 61 | DHCPServer.prototype.offer = function(packet, params) { 62 | if (params) { 63 | packet.yiaddr = params.yiaddr; 64 | packet.siaddr = params.siaddr; 65 | packet.options = params.options; 66 | } 67 | packet.op = packet.options[53] = 2; 68 | return this._send('offer', this.broadcast, packet); 69 | }; 70 | 71 | DHCPServer.prototype.ack = function(packet, params) { 72 | if (params) { 73 | packet.yiaddr = params.yiaddr; 74 | packet.siaddr = params.siaddr; 75 | packet.options = params.options; 76 | } 77 | packet.op = 2; 78 | packet.options[53] = 5; 79 | return this._send('ack', this.broadcast, packet); 80 | }; 81 | 82 | DHCPServer.prototype.nak = function(packet, params) { 83 | packet.op = 2; 84 | packet.options[53] = 6; 85 | return this._send('nak', packet.ciaddr, packet); 86 | }; 87 | 88 | DHCPServer.prototype.inform = function(packet, params) { 89 | if (params) { 90 | packet.yiaddr = params.yiaddr; 91 | packet.siaddr = params.siaddr; 92 | packet.options = params.options; 93 | } 94 | packet.op = 2; 95 | packet.options[53] = 5; 96 | return this._send('inform', packet.ciaddr, packet); 97 | }; 98 | 99 | DHCPServer.Packet = dhcp.Packet; 100 | module.exports = DHCPServer; 101 | 102 | -------------------------------------------------------------------------------- /lib/sprintf.js: -------------------------------------------------------------------------------- 1 | function sprintf () { 2 | // Return a formatted string 3 | // 4 | // version: 1107.2516 5 | // discuss at: http://phpjs.org/functions/sprintf // + original by: Ash Searle (http://hexmen.com/blog/) 6 | // + namespaced by: Michael White (http://getsprink.com) 7 | // + tweaked by: Jack 8 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 9 | // + input by: Paulo Freitas // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 10 | // + input by: Brett Zamir (http://brett-zamir.me) 11 | // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) 12 | // * example 1: sprintf("%01.2f", 123.1); 13 | // * returns 1: 123.10 // * example 2: sprintf("[%10s]", 'monkey'); 14 | // * returns 2: '[ monkey]' 15 | // * example 3: sprintf("[%'#10s]", 'monkey'); 16 | // * returns 3: '[####monkey]' 17 | var regex = /%%|%(\d+\$)?([\-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g; 18 | var a = arguments, 19 | i = 0, 20 | format = a[i++]; 21 | 22 | // pad() 23 | var pad = function (str, len, chr, leftJustify) { 24 | if (!chr) { 25 | chr = ' '; 26 | } 27 | var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr); 28 | return leftJustify ? str + padding : padding + str; 29 | }; 30 | 31 | // justify() 32 | var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) { 33 | var diff = minWidth - value.length; 34 | if (diff > 0) { 35 | if (leftJustify || !zeroPad) { 36 | value = pad(value, minWidth, customPadChar, leftJustify); 37 | } else { 38 | value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length); 39 | } 40 | } 41 | return value; 42 | }; 43 | // formatBaseX() 44 | var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) { 45 | // Note: casts negative numbers to positive ones 46 | var number = value >>> 0; 47 | prefix = prefix && number && { 48 | '2': '0b', 49 | '8': '0', 50 | '16': '0x' 51 | }[base] || ''; 52 | value = prefix + pad(number.toString(base), precision || 0, '0', false); 53 | return justify(value, prefix, leftJustify, minWidth, zeroPad); 54 | }; 55 | 56 | // formatString() 57 | var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) { 58 | if (precision !== null) { 59 | value = value.slice(0, precision); 60 | } 61 | return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar); 62 | }; 63 | 64 | // doFormat() 65 | var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) { 66 | var number; 67 | var prefix; 68 | var method; 69 | var textTransform; 70 | var value; 71 | if (substring == '%%') { 72 | return '%'; 73 | } 74 | 75 | // parse flags 76 | var leftJustify = false, 77 | positivePrefix = '', 78 | zeroPad = false, 79 | prefixBaseX = false, 80 | customPadChar = ' '; 81 | var flagsl = flags.length; 82 | for (var j = 0; flags && j < flagsl; j++) { 83 | switch (flags.charAt(j)) { 84 | case ' ': 85 | positivePrefix = ' '; 86 | break; 87 | case '+': 88 | positivePrefix = '+'; 89 | break; 90 | case '-': 91 | leftJustify = true; 92 | break; 93 | case "'": 94 | customPadChar = flags.charAt(j + 1); 95 | break; 96 | case '0': 97 | zeroPad = true; 98 | break; 99 | case '#': 100 | prefixBaseX = true; 101 | break; 102 | } 103 | } 104 | 105 | // parameters may be null, undefined, empty-string or real valued 106 | // we want to ignore null, undefined and empty-string values 107 | if (!minWidth) { 108 | minWidth = 0; 109 | } else if (minWidth == '*') { 110 | minWidth = +a[i++]; 111 | } else if (minWidth.charAt(0) == '*') { 112 | minWidth = +a[minWidth.slice(1, -1)]; 113 | } else { 114 | minWidth = +minWidth; 115 | } 116 | // Note: undocumented perl feature: 117 | if (minWidth < 0) { 118 | minWidth = -minWidth; 119 | leftJustify = true; 120 | } 121 | 122 | if (!isFinite(minWidth)) { 123 | throw new Error('sprintf: (minimum-)width must be finite'); 124 | } 125 | if (!precision) { 126 | precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined; 127 | } else if (precision == '*') { 128 | precision = +a[i++]; 129 | } else if (precision.charAt(0) == '*') { 130 | precision = +a[precision.slice(1, -1)]; 131 | } else { 132 | precision = +precision; 133 | } 134 | // grab value using valueIndex if required? 135 | value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++]; 136 | 137 | switch (type) { 138 | case 's': 139 | return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar); 140 | case 'c': 141 | return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad); 142 | case 'b': 143 | return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad); 144 | case 'o': 145 | return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad); 146 | case 'x': 147 | return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad); 148 | case 'X': 149 | return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase(); 150 | case 'u': 151 | return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad); 152 | case 'i': 153 | case 'd': 154 | number = (+value) | 0; 155 | prefix = number < 0 ? '-' : positivePrefix; 156 | value = prefix + pad(String(Math.abs(number)), precision, '0', false); 157 | return justify(value, prefix, leftJustify, minWidth, zeroPad); 158 | case 'e': 159 | case 'E': 160 | case 'f': 161 | case 'F': 162 | case 'g': 163 | case 'G': 164 | number = +value; 165 | prefix = number < 0 ? '-' : positivePrefix; 166 | method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())]; 167 | textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2]; 168 | value = prefix + Math.abs(number)[method](precision); 169 | return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform](); 170 | default: 171 | return substring; 172 | } 173 | }; 174 | 175 | return format.replace(regex, doFormat); 176 | } 177 | 178 | module.exports = sprintf; 179 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | var sprintf; 2 | 3 | sprintf = require("./sprintf"); 4 | 5 | module.exports = { 6 | /* 7 | Writes an array with bytes to the packets Buffer from the current position 8 | 9 | @param {Array} byteArray A simple array with uint8 values ([0x00, 0xFF]) 10 | @return {Packet} 11 | */ 12 | 13 | writeBytes: function(_buffer, byteArray, offset) { 14 | var i, _bytesWritten; 15 | i = 0; 16 | while (i < byteArray.length) { 17 | _buffer[offset++] = byteArray[i]; 18 | i++; 19 | } 20 | if (offset > _bytesWritten) { 21 | _bytesWritten = offset; 22 | } 23 | return this; 24 | }, 25 | /* 26 | Write a 4-byte integer 27 | 28 | @param {integer} integer The value to write to the packet 29 | @return {Packet} 30 | */ 31 | 32 | writeInt32: function(_buffer, integer, offset) { 33 | _buffer[offset++] = integer >>> 24; 34 | _buffer[offset++] = integer >>> 16; 35 | _buffer[offset++] = integer >>> 8; 36 | _buffer[offset++] = integer; 37 | return offset; 38 | }, 39 | /* 40 | Write a 2-byte integer 41 | 42 | @param {integer} integer The value to write to the packet 43 | @return {Packet} 44 | */ 45 | 46 | writeInt16: function(_buffer, integer, offset) { 47 | _buffer[offset++] = integer >>> 8; 48 | _buffer[offset++] = integer; 49 | return offset; 50 | }, 51 | /* 52 | Write a 1-byte integer 53 | 54 | @param {integer} integer The value to write to the packet 55 | @return {Packet} 56 | */ 57 | 58 | writeInt8: function(_buffer, integer, offset) { 59 | _buffer[offset++] = integer; 60 | return offset; 61 | }, 62 | /* 63 | Reads the given length of bytes from the packets Buffer 64 | and optionally (if the copy argument is true) creates a real copy of the Buffer 65 | 66 | @param {Integer} length 67 | @param {Boolean} copy 68 | @return {Buffer} 69 | */ 70 | 71 | readBytes: function(_buffer, length, copy, offset) { 72 | var bufCopy; 73 | if (copy) { 74 | bufCopy = new Buffer(length); 75 | _buffer.copy(bufCopy, 0, offset, (offset += length)); 76 | return bufCopy; 77 | } else { 78 | return _buffer.slice(offset, (offset += length)); 79 | } 80 | }, 81 | /* 82 | Reads a 4-byte integer from the packet 83 | 84 | @return {Integer} 85 | */ 86 | 87 | readInt32: function(_buffer, offset) { 88 | return (_buffer[offset++] << 24) | (_buffer[offset++] << 16) | (_buffer[offset++] << 8) | _buffer[offset++]; 89 | }, 90 | /* 91 | Reads a 2-byte integer from the packet 92 | 93 | @return {Integer} 94 | */ 95 | 96 | readInt16: function(_buffer, offset) { 97 | return (_buffer[offset++] << 8) | _buffer[offset++]; 98 | }, 99 | /* 100 | Reads a 1-byte integer from the packet 101 | 102 | @return {Integer} 103 | */ 104 | 105 | readInt8: function(_buffer, offset) { 106 | return _buffer[offset++]; 107 | }, 108 | readString: function(buf) { 109 | var j, s; 110 | s = ""; 111 | j = 0; 112 | while (j < buf.length) { 113 | s += String.fromCharCode(buf[j]); 114 | j++; 115 | } 116 | return s; 117 | }, 118 | readHex: function(buf) { 119 | var j, s; 120 | s = ""; 121 | j = 0; 122 | while (j < buf.length) { 123 | s += sprintf("%02x", b[j]); 124 | j++; 125 | } 126 | return s; 127 | }, 128 | readHexAddress: function(buf) { 129 | var j, s; 130 | s = []; 131 | j = 0; 132 | while (j < buf.length) { 133 | s.push(sprintf("%02d", buf[j])); 134 | j++; 135 | } 136 | return s.join(":"); 137 | }, 138 | readIp: function(buffer, offset) { 139 | var stop; 140 | if (offset === null) { 141 | offset = 0; 142 | } 143 | if(offset > buffer.length){ 144 | return undefined; 145 | } 146 | if (0 === buffer.readUInt8(offset)) { 147 | return undefined; 148 | } else { 149 | stop = offset + 4; 150 | return ((function() { 151 | var _results; 152 | _results = []; 153 | while (offset < stop) { 154 | _results.push(buffer.readUInt8(offset++)); 155 | } 156 | return _results; 157 | })()).join('.'); 158 | } 159 | }, 160 | readMacAddress: function(buffer) { 161 | var byte; 162 | return ((function() { 163 | var _i, _len, _results; 164 | _results = []; 165 | for (_i = 0, _len = buffer.length; _i < _len; _i++) { 166 | byte = buffer[_i]; 167 | _results.push((byte + 0x100).toString(16).substr(-2)); 168 | } 169 | return _results; 170 | })()).join(':'); 171 | }, 172 | writeTimeOffset: function(buf, num, offsetHours, offset) { 173 | buf[offset++] = 0; 174 | return offset; 175 | }, 176 | writeIp: function(buf, num, ip, offset) { 177 | buf[offset++] = num; 178 | buf[offset++] = 4; 179 | ip.split(".").forEach(function(item) { 180 | buf[offset++] = item; 181 | }); 182 | return offset; 183 | }, 184 | writeString: function(buf, num, hostname, offset) { 185 | var charArr; 186 | charArr = hostname.split(""); 187 | buf[offset++] = num; 188 | buf[offset++] = charArr.length; 189 | charArr.forEach(function(chr) { 190 | buf[offset++] = chr.charCodeAt(); 191 | }); 192 | return offset; 193 | } 194 | }; 195 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-dhcpd", 3 | "description": "A DHCP (server) implementation purely written in JavaScript with node.js", 4 | "version": "0.0.1", 5 | "author": "glaszig ", 6 | "contributors": [ 7 | { "name": "Scott McWhirter", "email": "scott@cloudtone.ca", "url": "http://github.com/cloudtone" } 8 | ], 9 | "keywords": ["dhcp"], 10 | "main": "./lib/server", 11 | "engines": { "node": ">= 0.8.0" }, 12 | "dependencies": { 13 | "sprintf": ">= 0.1.1" 14 | }, 15 | "devDependencies": { 16 | "sqlite3": "= 2.1.5" 17 | } 18 | } 19 | --------------------------------------------------------------------------------