diff --git a/.gitignore b/.gitignore index b4b8eb20d..7f7017231 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swo +*.un~ diff --git a/benchmark/insert.js b/benchmark/insert.js new file mode 100644 index 000000000..6dc573461 --- /dev/null +++ b/benchmark/insert.js @@ -0,0 +1,42 @@ +require('../test/common'); +var Client = require('mysql/client'), + client = Client(TEST_CONFIG); + +client.connect(); + +client.query('CREATE DATABASE '+TEST_DB, function(err) { + if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) { + throw err; + } +}); +client.query('USE '+TEST_DB); +client.query( + 'CREATE TEMPORARY TABLE '+TEST_TABLE+' ('+ + 'id INT(11) AUTO_INCREMENT, '+ + 'title VARCHAR(255), '+ + 'text TEXT, '+ + 'created DATETIME, '+ + 'PRIMARY KEY (id));', + function(err) { + if (err) throw err; + + var start = +new Date, inserts = 0, total = 10000; + console.log('performing %d inserts ...\n', total); + + function insertOne() { + client.query('INSERT INTO '+TEST_TABLE+' SET title = ?', ['super'], function() { + inserts++; + if (inserts < total) { + insertOne(); + } else { + var duration = (+new Date - start) / 1000, + insertsPerSecond = inserts / duration; + + console.log('%d inserts / second', insertsPerSecond.toFixed(2)); + client.end(); + } + }); + } + insertOne(); + } +); diff --git a/lib/mysql/auth.js b/lib/mysql/auth.js index 09e2d6c1c..6077560de 100644 --- a/lib/mysql/auth.js +++ b/lib/mysql/auth.js @@ -30,3 +30,135 @@ exports.token = function(password, scramble) { var stage3 = sha1(scramble.toString('binary') + stage2); return xor(stage3, stage1); }; + +// This is a port of sql/password.c:hash_password which needs to be used for +// pre-4.1 passwords. +exports.hashPassword = function(password) { + var nr = [0x5030, 0x5735], + add = 7, + nr2 = [0x1234, 0x5671], + result = new Buffer(8); + + if (typeof password == 'string'){ + password = new Buffer(password); + } + + for (var i = 0; i < password.length; i++) { + var c = password[i]; + if (c == 32 || c == 9) { + // skip space in password + continue; + } + + // nr^= (((nr & 63)+add)*c)+ (nr << 8); + // nr = xor(nr, add(mul(add(and(nr, 63), add), c), shl(nr, 8))) + nr = this.xor32(nr, this.add32(this.mul32(this.add32(this.and32(nr, [0,63]), [0,add]), [0,c]), this.shl32(nr, 8))); + + // nr2+=(nr2 << 8) ^ nr; + // nr2 = add(nr2, xor(shl(nr2, 8), nr)) + nr2 = this.add32(nr2, this.xor32(this.shl32(nr2, 8), nr)); + + // add+=tmp; + add += c; + } + + this.int31Write(result, nr, 0); + this.int31Write(result, nr2, 4); + + return result; +}; + +exports.randomInit = function(seed1, seed2) { + return { + max_value: 0x3FFFFFFF, + max_value_dbl: 0x3FFFFFFF, + seed1: seed1 % 0x3FFFFFFF, + seed2: seed2 % 0x3FFFFFFF, + }; +}; + +exports.myRnd = function(r){ + r.seed1 = (r.seed1 * 3 + r.seed2) % r.max_value; + r.seed2 = (r.seed1 + r.seed2 + 33) % r.max_value; + + return r.seed1 / r.max_value_dbl; +}; + +exports.scramble323 = function(message, password) { + var to = new Buffer(8), + hashPass = this.hashPassword(password), + hashMessage = this.hashPassword(message.slice(0, 8)), + seed1 = this.int32Read(hashPass, 0) ^ this.int32Read(hashMessage, 0), + seed2 = this.int32Read(hashPass, 4) ^ this.int32Read(hashMessage, 4), + r = this.randomInit(seed1, seed2); + + for (var i = 0; i < 8; i++){ + to[i] = Math.floor(this.myRnd(r) * 31) + 64; + } + var extra = (Math.floor(this.myRnd(r) * 31)); + + for (var i = 0; i < 8; i++){ + to[i] ^= extra; + } + + return to; +}; + +exports.fmt32 = function(x){ + var a = x[0].toString(16), + b = x[1].toString(16); + + if (a.length == 1) a = '000'+a; + if (a.length == 2) a = '00'+a; + if (a.length == 3) a = '0'+a; + if (b.length == 1) b = '000'+b; + if (b.length == 2) b = '00'+b; + if (b.length == 3) b = '0'+b; + return '' + a + '/' + b; +} + +exports.xor32 = function(a,b){ + return [a[0] ^ b[0], a[1] ^ b[1]]; +} + +exports.add32 = function(a,b){ + var w1 = a[1] + b[1], + w2 = a[0] + b[0] + ((w1 & 0xFFFF0000) >> 16); + + return [w2 & 0xFFFF, w1 & 0xFFFF]; +} + +exports.mul32 = function(a,b){ + // based on this example of multiplying 32b ints using 16b + // http://www.dsprelated.com/showmessage/89790/1.php + var w1 = a[1] * b[1], + w2 = (((a[1] * b[1]) >> 16) & 0xFFFF) + ((a[0] * b[1]) & 0xFFFF) + (a[1] * b[0] & 0xFFFF); + + return [w2 & 0xFFFF, w1 & 0xFFFF]; +} + +exports.and32 = function(a,b){ + return [a[0] & b[0], a[1] & b[1]] +} + +exports.shl32 = function(a,b){ + // assume b is 16 or less + var w1 = a[1] << b, + w2 = (a[0] << b) | ((w1 & 0xFFFF0000) >> 16); + + return [w2 & 0xFFFF, w1 & 0xFFFF]; +} + +exports.int31Write = function(buffer, number, offset) { + buffer[offset] = (number[0] >> 8) & 0x7F; + buffer[offset + 1] = (number[0]) & 0xFF; + buffer[offset + 2] = (number[1] >> 8) & 0xFF; + buffer[offset + 3] = (number[1]) & 0xFF; +}; + +exports.int32Read = function(buffer, offset){ + return (buffer[offset] << 24) + + (buffer[offset+1] << 16) + + (buffer[offset+2] << 8) + + (buffer[offset+3]); +}; diff --git a/lib/mysql/client.js b/lib/mysql/client.js index b609377f0..66ac060c1 100644 --- a/lib/mysql/client.js +++ b/lib/mysql/client.js @@ -19,13 +19,14 @@ function Client(config) { this.port = 3306; this.user = null; this.password = null; - this.database = null; + this.database = ''; this.flags = Client.defaultFlags; this.maxPacketSize = 0x01000000; this.charsetNumber = 8; this.debug = false; + this._greeting = null; this._queue = []; this._connection = null; this._parser = null; @@ -194,6 +195,11 @@ Client.prototype._handlePacket = function(packet) { return; } + if (packet.type == Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET) { + this._sendOldAuth(this._greeting); + return; + } + var type = packet.type, task = this._queue[0], delegate = (task) @@ -239,6 +245,11 @@ Client.prototype._sendAuth = function(greeting) { packet.writeNullTerminated(this.database); this.write(packet); + + // Keep a reference to the greeting packet. We might receive a + // USE_OLD_PASSWORD_PROTOCOL_PACKET as a response, in which case we will need + // the greeting packet again. See _sendOldAuth() + this._greeting = greeting; }; Client._packetToUserObject = function(packet) { @@ -284,6 +295,22 @@ Client.prototype._debugPacket = function(packet) { console.log('<- %s: %j', packetName, packet); }; +Client.prototype._sendOldAuth = function(greeting) { + var token = auth.scramble323(greeting.scrambleBuffer, this.password), + packetSize = ( + token.length + 1 + ), + packet = new OutgoingPacket(packetSize, greeting.number+3); + + // I could not find any official documentation for this, but from sniffing + // the mysql command line client, I think this is the right way to send the + // scrambled token after receiving the USE_OLD_PASSWORD_PROTOCOL_PACKET. + packet.write(token); + packet.writeFiller(1); + + this.write(packet); +}; + // Client Flags Client.LONG_PASSWORD = 1; Client.FOUND_ROWS = 2; diff --git a/lib/mysql/parser.js b/lib/mysql/parser.js index 0ab523585..077a0e88f 100644 --- a/lib/mysql/parser.js +++ b/lib/mysql/parser.js @@ -12,6 +12,7 @@ function Parser() { this.state = Parser.PACKET_LENGTH; this.packet = null; this.greeted = false; + this.authenticated = false; this.receivingFieldPackets = false; this.receivingRowPackets = false; @@ -211,7 +212,14 @@ Parser.prototype.write = function(buffer) { break; } + if (c == 0xfe && !this.authenticated) { + packet.type = Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET; + break; + } + if (c === 0x00) { + // after the first OK PACKET, we are authenticated + this.authenticated = true; packet.type = Parser.OK_PACKET; advance(Parser.AFFECTED_ROWS) break; @@ -606,3 +614,4 @@ Parser.ROW_DATA_PACKET = p++; Parser.ROW_DATA_BINARY_PACKET = p++; Parser.OK_FOR_PREPARED_STATEMENT_PACKET = p++; Parser.PARAMETER_PACKET = p++; +Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET = p++; diff --git a/package.json b/package.json index 35f588028..a990106c8 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name" : "mysql" -, "version": "0.3.0" +, "version": "0.4.0" , "dependencies": {"gently": ">=0.8.0"} , "main" : "./lib/mysql" } diff --git a/test/common.js b/test/common.js index 08e45acbb..cf91bdc8f 100644 --- a/test/common.js +++ b/test/common.js @@ -3,12 +3,12 @@ var path = require('path') require.paths.unshift(path.dirname(__dirname)+'/lib'); +global.TEST_DB = 'node_mysql_test'; global.TEST_CONFIG = { host: 'localhost', port: 3306, user: 'root', - password: 'root', - database: 'node_mysql_test', + password: 'root' }; global.TEST_TABLE = 'posts'; diff --git a/test/fixture/libmysql_password.c b/test/fixture/libmysql_password.c new file mode 100644 index 000000000..3e19c83eb --- /dev/null +++ b/test/fixture/libmysql_password.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#define SCRAMBLE_LENGTH_323 8 + +typedef unsigned long ulong; +typedef unsigned int uint; +typedef unsigned char uchar; + +struct rand_struct { + unsigned long seed1,seed2,max_value; + double max_value_dbl; +}; + +void hash_password(ulong *result, const char *password, uint password_len) +{ + register ulong nr=1345345333L, add=7, nr2=0x12345671L; + ulong tmp; + const char *password_end= password + password_len; + for (; password < password_end; password++) + { + if (*password == ' ' || *password == '\t') + continue; /* skip space in password */ + tmp= (ulong) (uchar) *password; + nr^= (((nr & 63)+add)*tmp)+ (nr << 8); + nr2+=(nr2 << 8) ^ nr; + add+=tmp; + } + result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */; + result[1]=nr2 & (((ulong) 1L << 31) -1L); +} + +void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2) +{ /* For mysql 3.21.# */ +#ifdef HAVE_purify + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ +#endif + rand_st->max_value= 0x3FFFFFFFL; + rand_st->max_value_dbl=(double) rand_st->max_value; + rand_st->seed1=seed1%rand_st->max_value ; + rand_st->seed2=seed2%rand_st->max_value; +} + +double my_rnd(struct rand_struct *rand_st) +{ + rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value; + rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value; + return (((double) rand_st->seed1)/rand_st->max_value_dbl); +} + +void scramble_323(char *to, const char *message, const char *password) +{ + struct rand_struct rand_st; + ulong hash_pass[2], hash_message[2]; + + if (password && password[0]) + { + char extra, *to_start=to; + const char *message_end= message + SCRAMBLE_LENGTH_323; + hash_password(hash_pass,password, (uint) strlen(password)); + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); + for (; message < message_end; message++) + *to++= (char) (floor(my_rnd(&rand_st)*31)+64); + extra=(char) (floor(my_rnd(&rand_st)*31)); + while (to_start != to) + *(to_start++)^=extra; + } + *to= 0; +} + +int main() { + const char password1[] = "root"; + const char password2[] = "long password test"; + const char password3[] = "saf789yasfbsd89f"; + ulong result[2]; + char scrm[9]; // SCRAMBLE_LENGTH_323+1 + struct rand_struct rand_st; + int i; + + // test hash_password + hash_password((ulong*)result, password1, strlen(password1)); + printf("hash_password(\"%s\") = %08x%08x\n", password1, result[0], result[1]); + + hash_password((ulong*)result, password2, strlen(password2)); + printf("hash_password(\"%s\") = %08x%08x\n", password2, result[0], result[1]); + + hash_password((ulong*)result, password3, strlen(password3)); + printf("hash_password(\"%s\") = %08x%08x\n", password3, result[0], result[1]); + + + // test randominit + randominit(&rand_st, 0, 0); + printf("randominit(0x00000000,0x00000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2); + + randominit(&rand_st, 0xFFFF, 0xFFFF); + printf("randominit(0x0000FFFF,0x0000FFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2); + + randominit(&rand_st, 0x50000000, 0x50000000); + printf("randominit(0x50000000,0x50000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2); + + randominit(&rand_st, 0xFFFFFFFF, 0xFFFFFFFF); + printf("randominit(0xFFFFFFFF,0xFFFFFFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2); + + + // test my_rnd + randominit(&rand_st, 3252345, 7149734); + printf("randominit(3252345, 7149734) = %08x, %08x\n", rand_st.seed1, rand_st.seed2); + for (i=0; i<10; i++){ + printf("my_rnd() : %.16f\n", my_rnd(&rand_st)); + } + + + // test scramble_323 + scramble_323(scrm, "8bytesofstuff", "root"); + printf("scramble323(8bytesofstuff, root): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]); + + scramble_323(scrm, "e8cf00cec9ec825af22", "saf789yasfbsd"); + printf("scramble323(e8cf00cec9ec825af22, saf789yasfbsd): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]); + + return 23; +} + diff --git a/test/simple/test-auth.js b/test/simple/test-auth.js index ac89dc686..dad5856e6 100644 --- a/test/simple/test-auth.js +++ b/test/simple/test-auth.js @@ -45,3 +45,74 @@ test(function token() { assert.deepEqual(auth.token(null, SCRAMBLE), new Buffer(0)); })(); }); + +test(function hashPassword() { + function verify(password, bytes){ + var expected = new Buffer(bytes); + var actual = auth.hashPassword(password); + + assert.deepEqual(actual, expected); + } + + verify('root', [0x67, 0x45, 0x7E, 0x22, 0x6a, 0x1a, 0x15, 0xbd]); + verify('long password test', [0x6c, 0x24, 0x68, 0x41, 0x2c, 0xa6, 0x86, 0x56]); + verify('saf789yasfbsd89f', [0x6c, 0x9b, 0x2f, 0x07, 0x17, 0xeb, 0x95, 0xc6]); +}); + + +test(function randomInit() { + function verify(in1, in2, out1, out2){ + var r = auth.randomInit(in1, in2); + assert.equal(out1, r.seed1); + assert.equal(out2, r.seed2); + } + + verify(0x00000000, 0x00000000, 0x00000000, 0x00000000); + verify(0x0000FFFF, 0x0000FFFF, 0x0000ffff, 0x0000ffff); + verify(0x50000000, 0x50000000, 0x10000001, 0x10000001); + verify(0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, 0x00000003); + verify(3252345, 7149734, 0x0031a079, 0x006d18a6); +}); + +test(function myRnd() { + function verifySequence(seed1, seed2, expected){ + var r = auth.randomInit(seed1, seed2); + for (var i = 0; i < expected.length; i++){ + var n = auth.myRnd(r); + + // we will test to 14 digits, since + // we only ever use this function mutliplied + // by small numbers anyway + + var a = ':'+n; + var b = ':'+expected[i]; + + assert.equal(a.substr(1, 16), b.substr(1, 16)); + } + } + + verifySequence(3252345, 7149734, [ + 0.0157456556481734, + 0.0696413620092360, + 0.3009698738353047, + 0.2959253138824602, + 0.5767169786400320, + 0.9958089822864243, + 0.2488940062456708, + 0.2570431151027261, + 0.5385335875102631, + 0.9215386229767824, + ]); +}); + +test(function scramble323() { + function verify(message, password, bytes){ + var expected = new Buffer(bytes); + var actual = auth.scramble323(new Buffer(message), password); + + assert.deepEqual(actual, expected); + } + + verify('8bytesofstuff', 'root', [0x5a, 0x4d, 0x46, 0x47, 0x43, 0x53, 0x58, 0x5f]); + verify('e8cf00cec9ec825af22', 'saf789yasfbsd', [0x4d, 0x54, 0x5b, 0x47, 0x5f, 0x52, 0x4d, 0x45]); +}); diff --git a/test/simple/test-client.js b/test/simple/test-client.js index a0d858ac4..7ab26c949 100644 --- a/test/simple/test-client.js +++ b/test/simple/test-client.js @@ -26,7 +26,7 @@ test(function constructor() { assert.strictEqual(client.port, 3306); assert.strictEqual(client.user, null); assert.strictEqual(client.password, null); - assert.strictEqual(client.database, null); + assert.strictEqual(client.database, ''); assert.strictEqual(client.debug, false); @@ -34,6 +34,7 @@ test(function constructor() { assert.strictEqual(client.maxPacketSize, 0x01000000); assert.strictEqual(client.charsetNumber, 8); + assert.strictEqual(client._greeting, null); assert.deepEqual(client._queue, []); assert.strictEqual(client._connection, null); assert.strictEqual(client._parser, null); @@ -363,6 +364,17 @@ test(function _handlePacket() { client._handlePacket(PACKET); })(); + (function testUseOldPasswordProtocol() { + client._greeting = {}; + var PACKET = {type: Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET}; + + gently.expect(client, '_sendOldAuth', function (greeting) { + assert.strictEqual(greeting, client._greeting); + }); + + client._handlePacket(PACKET); + })(); + (function testNormalOk() { var PACKET = {type: Parser.OK_PACKET}; @@ -437,9 +449,9 @@ test(function _handlePacket() { })(); }); -test(function _sendPacket() { +test(function _sendAuth() { var GREETING = {scrambleBuffer: new Buffer(20), number: 1}, - TOKEN = new Buffer(8), + TOKEN = new Buffer(20), PACKET; client.user = 'root'; @@ -500,6 +512,7 @@ test(function _sendPacket() { }); client._sendAuth(GREETING); + assert.strictEqual(client._greeting, GREETING); }); test(function _packetToUserObject() { @@ -541,3 +554,39 @@ test(function _packetToUserObject() { assert.equal(err.errorNumber, undefined); })(); }); + +test(function _sendOldAuth() { + var GREETING = {scrambleBuffer: new Buffer(8), number: 1}, + TOKEN = new Buffer(8), + PACKET; + + client.user = 'root'; + client.password = 'hello world'; + + gently.expect(HIJACKED['./auth'], 'scramble323', function(scramble, password) { + assert.strictEqual(scramble, GREETING.scrambleBuffer); + assert.strictEqual(password, client.password); + return TOKEN; + }); + + gently.expect(OutgoingPacketStub, 'new', function(size, number) { + assert.equal(size, TOKEN.length + 1); + + assert.equal(number, GREETING.number + 3); + PACKET = this; + + gently.expect(PACKET, 'write', function(token) { + assert.strictEqual(token, TOKEN); + }); + + gently.expect(PACKET, 'writeFiller', function(bytes) { + assert.strictEqual(bytes, 1); + }); + + gently.expect(client, 'write', function(packet) { + assert.strictEqual(packet, PACKET); + }); + }); + + client._sendOldAuth(GREETING); +}); diff --git a/test/simple/test-parser.js b/test/simple/test-parser.js index ccdc9d00e..9ea60161d 100644 --- a/test/simple/test-parser.js +++ b/test/simple/test-parser.js @@ -15,6 +15,7 @@ test(function constructor() { assert.strictEqual(parser.state, Parser.PACKET_LENGTH); assert.strictEqual(parser.packet, null); assert.strictEqual(parser.greeted, false); + assert.strictEqual(parser.authenticated, false); assert.strictEqual(parser.receivingFieldPackets, false); assert.strictEqual(parser.receivingRowPackets, false); assert.strictEqual(parser._lengthCodedLength, null); @@ -114,6 +115,18 @@ test(function write() { assert.strictEqual(parser.packet, null); })(); + (function testUseOldPasswordProtocolPacket() { + parser.write(new Buffer([1, 0, 0, 1])); + + gently.expect(parser, 'emit', function(event, val) { + assert.equal(event, 'packet'); + assert.equal(val.type, Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET); + }); + + parser.write(new Buffer([254])); + })(); + + (function testErrorPacket() { parser.write(new Buffer([12, 0, 0, 1])); assert.equal(parser.state, Parser.FIELD_COUNT); @@ -151,6 +164,7 @@ test(function write() { parser.write(new Buffer([0x00])); assert.equal(packet.type, Parser.OK_PACKET); + assert.equal(parser.authenticated, true); assert.equal(parser.state, Parser.AFFECTED_ROWS); parser.write(new Buffer([252, 17, 23])); diff --git a/test/system/test-client-connect.js b/test/system/test-client-connect.js index 0be5b06a7..ad36a3c2e 100644 --- a/test/system/test-client-connect.js +++ b/test/system/test-client-connect.js @@ -1,13 +1,10 @@ require('../common'); -var client = require('mysql').Client(TEST_CONFIG) - , gently = new Gently(); - -// our test db does not exist yet, so don't try to connect to it -client.database = ''; +var client = require('mysql').Client(TEST_CONFIG), + gently = new Gently(); client.connect(gently.expect(function connectCb(err, result) { if (err) throw err; assert.strictEqual(result.affectedRows, 0); client.end(); -})); \ No newline at end of file +})); diff --git a/test/system/test-client-query-calculated-fields.js b/test/system/test-client-query-calculated-fields.js index b40f6a7bc..4217351d7 100644 --- a/test/system/test-client-query-calculated-fields.js +++ b/test/system/test-client-query-calculated-fields.js @@ -1,10 +1,7 @@ require('../common'); -var Client = require('mysql').Client, - client = Client(TEST_CONFIG), +var client = require('mysql').Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it -client.database = ''; client.connect(); client.query('SELECT 1 as field_a, 2 as field_b', function(err, results) { diff --git a/test/system/test-client-query-empty.js b/test/system/test-client-query-empty.js index cb99062be..8def3a635 100644 --- a/test/system/test-client-query-empty.js +++ b/test/system/test-client-query-empty.js @@ -1,10 +1,7 @@ require('../common'); -var Client = require('mysql').Client, - client = Client(TEST_CONFIG), +var client = require('mysql').Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it -client.database = ''; client.connect(); client.query('SELECT "" as field_a', function(err, results) { diff --git a/test/system/test-client-query-long-fields.js b/test/system/test-client-query-long-fields.js index 8486d22e9..b1a7810d3 100644 --- a/test/system/test-client-query-long-fields.js +++ b/test/system/test-client-query-long-fields.js @@ -1,9 +1,7 @@ require('../common'); -var Client = require('mysql').Client, - client = Client(TEST_CONFIG), +var client = require('mysql').Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it client.connect(); function makeString(length) { diff --git a/test/system/test-client-query-null.js b/test/system/test-client-query-null.js index 4d672673e..463d98d01 100644 --- a/test/system/test-client-query-null.js +++ b/test/system/test-client-query-null.js @@ -1,10 +1,7 @@ require('../common'); -var Client = require('mysql').Client, - client = Client(TEST_CONFIG), +var client = require('mysql').Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it -client.database = ''; client.connect(); client.query('SELECT NULL as field_a, NULL as field_b', function(err, results) { diff --git a/test/system/test-client-query.js b/test/system/test-client-query.js index fd3e5c61b..54e90b678 100644 --- a/test/system/test-client-query.js +++ b/test/system/test-client-query.js @@ -3,13 +3,10 @@ var Client = require('mysql').Client, client = Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it -client.database = ''; - client.connect(); client.query( - 'CREATE DATABASE '+TEST_CONFIG.database, + 'CREATE DATABASE '+TEST_DB, gently.expect(function createDbCb(err) { if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) { throw err; @@ -18,7 +15,7 @@ client.query( ); client.query( - 'USE '+TEST_CONFIG.database, + 'USE '+TEST_DB, gently.expect(function useDbCb(err) { if (err) { throw err; diff --git a/test/system/test-client-sequential-query.js b/test/system/test-client-sequential-query.js index b5049a06a..239d54944 100644 --- a/test/system/test-client-sequential-query.js +++ b/test/system/test-client-sequential-query.js @@ -1,10 +1,7 @@ require('../common'); -var Client = require('mysql').Client, - client = Client(TEST_CONFIG), +var client = require('mysql').Client(TEST_CONFIG), gently = new Gently(); -// our test db might not exist yet, so don't try to connect to it -client.database = ''; client.connect(); client.query('SELECT 1 as field_a, 2 as field_b', function(err, results) { diff --git a/tool/pcap-mysql.js b/tool/pcap-mysql.js new file mode 100755 index 000000000..7810b0e8d --- /dev/null +++ b/tool/pcap-mysql.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node + +var sys = require("sys"), + pcap = require("pcap"), + mysqlPort = parseInt(process.argv[3]) || 3306, + pcap_session = pcap.createSession(process.argv[2] || '', 'tcp port '+mysqlPort); + +sys.puts('This tool allows to reverse engineer the mysql procotocol using node-pcap.'); +sys.puts(''); +sys.puts('Available devices (active one is denoted by *):'); + +// Print all devices, currently listening device prefixed with an asterisk +pcap_session.findalldevs().forEach(function (dev) { + sys.print(' '); + if (pcap_session.device_name === dev.name) { + sys.print("* "); + } + sys.print(dev.name + " "); + if (dev.addresses.length > 0) { + dev.addresses.forEach(function (address) { + sys.print(address.addr + "/" + address.netmask); + }); + sys.print("\n"); + } else { + sys.print("no address\n"); + } +}); + +sys.puts(''); +sys.puts('Execute `./pcap-mysql.js ` to listen on another device.'); +sys.puts(''); + +// Listen for packets, decode them, and feed the simple printer. No tricks. +pcap_session.on('packet', function (raw_packet) { + var packet = pcap.decode.packet(raw_packet); + //sys.puts(pcap.print.packet(packet)); + var tcp = packet.link.ip.tcp; + if (!tcp.data) { + return; + } + + if (tcp.sport == mysqlPort) { + sys.puts('<- '+tcp.data.inspect()); + } else { + sys.puts('-> '+tcp.data.inspect()); + } +}); +