packet.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. 'use strict';
  2. const Errors = require('../misc/errors');
  3. const Iconv = require('iconv-lite');
  4. const Long = require('long');
  5. const moment = require('moment-timezone');
  6. /**
  7. * Object to easily parse buffer.
  8. *
  9. */
  10. class Packet {
  11. constructor(buf, pos, end) {
  12. this.buf = buf;
  13. this.pos = pos;
  14. this.end = end;
  15. }
  16. skip(n) {
  17. this.pos += n;
  18. }
  19. readGeometry(dataTypeName) {
  20. const geoBuf = this.readBufferLengthEncoded();
  21. if (geoBuf === null || geoBuf.length === 0) {
  22. if (dataTypeName) {
  23. switch (dataTypeName) {
  24. case 'point':
  25. return { type: 'Point' };
  26. case 'linestring':
  27. return { type: 'LineString' };
  28. case 'polygon':
  29. return { type: 'Polygon' };
  30. case 'multipoint':
  31. return { type: 'MultiPoint' };
  32. case 'multilinestring':
  33. return { type: 'MultiLineString' };
  34. case 'multipolygon':
  35. return { type: 'MultiPolygon' };
  36. default:
  37. return { type: dataTypeName };
  38. }
  39. }
  40. return null;
  41. }
  42. let geoPos = 4;
  43. return readGeometryObject(false);
  44. function parseCoordinates(byteOrder) {
  45. geoPos += 16;
  46. const x = byteOrder ? geoBuf.readDoubleLE(geoPos - 16) : geoBuf.readDoubleBE(geoPos - 16);
  47. const y = byteOrder ? geoBuf.readDoubleLE(geoPos - 8) : geoBuf.readDoubleBE(geoPos - 8);
  48. return [x, y];
  49. }
  50. function readGeometryObject(inner) {
  51. const byteOrder = geoBuf[geoPos++];
  52. const wkbType = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
  53. geoPos += 4;
  54. switch (wkbType) {
  55. case 1: //wkbPoint
  56. const coords = parseCoordinates(byteOrder);
  57. if (inner) return coords;
  58. return {
  59. type: 'Point',
  60. coordinates: coords
  61. };
  62. case 2: //wkbLineString
  63. const pointNumber = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
  64. geoPos += 4;
  65. let coordinates = [];
  66. for (let i = 0; i < pointNumber; i++) {
  67. coordinates.push(parseCoordinates(byteOrder));
  68. }
  69. if (inner) return coordinates;
  70. return {
  71. type: 'LineString',
  72. coordinates: coordinates
  73. };
  74. case 3: //wkbPolygon
  75. let polygonCoordinates = [];
  76. const numRings = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
  77. geoPos += 4;
  78. for (let ring = 0; ring < numRings; ring++) {
  79. const pointNumber = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
  80. geoPos += 4;
  81. let linesCoordinates = [];
  82. for (let i = 0; i < pointNumber; i++) {
  83. linesCoordinates.push(parseCoordinates(byteOrder));
  84. }
  85. polygonCoordinates.push(linesCoordinates);
  86. }
  87. if (inner) return polygonCoordinates;
  88. return {
  89. type: 'Polygon',
  90. coordinates: polygonCoordinates
  91. };
  92. case 4: //wkbMultiPoint
  93. return {
  94. type: 'MultiPoint',
  95. coordinates: parseGeomArray(byteOrder, true)
  96. };
  97. case 5: //wkbMultiLineString
  98. return {
  99. type: 'MultiLineString',
  100. coordinates: parseGeomArray(byteOrder, true)
  101. };
  102. case 6: //wkbMultiPolygon
  103. return {
  104. type: 'MultiPolygon',
  105. coordinates: parseGeomArray(byteOrder, true)
  106. };
  107. case 7: //wkbGeometryCollection
  108. return {
  109. type: 'GeometryCollection',
  110. geometries: parseGeomArray(byteOrder, false)
  111. };
  112. }
  113. return null;
  114. }
  115. function parseGeomArray(byteOrder, inner) {
  116. let coordinates = [];
  117. const number = byteOrder ? geoBuf.readInt32LE(geoPos) : geoBuf.readInt32BE(geoPos);
  118. geoPos += 4;
  119. for (let i = 0; i < number; i++) {
  120. coordinates.push(readGeometryObject(inner));
  121. }
  122. return coordinates;
  123. }
  124. }
  125. peek() {
  126. return this.buf[this.pos];
  127. }
  128. remaining() {
  129. return this.end - this.pos > 0;
  130. }
  131. readUInt8() {
  132. return this.buf[this.pos++];
  133. }
  134. readUInt16() {
  135. return this.buf[this.pos++] + (this.buf[this.pos++] << 8);
  136. }
  137. readUInt24() {
  138. return this.buf[this.pos++] + (this.buf[this.pos++] << 8) + (this.buf[this.pos++] << 16);
  139. }
  140. readUInt32() {
  141. return (
  142. this.buf[this.pos++] +
  143. (this.buf[this.pos++] << 8) +
  144. (this.buf[this.pos++] << 16) +
  145. this.buf[this.pos++] * 0x1000000
  146. );
  147. }
  148. readInt32() {
  149. return (
  150. this.buf[this.pos++] +
  151. (this.buf[this.pos++] << 8) +
  152. (this.buf[this.pos++] << 16) +
  153. (this.buf[this.pos++] << 24)
  154. );
  155. }
  156. readInt32LE() {
  157. return (
  158. (this.buf[this.pos++] << 24) +
  159. (this.buf[this.pos++] << 16) +
  160. (this.buf[this.pos++] << 8) +
  161. this.buf[this.pos++]
  162. );
  163. }
  164. readInt64() {
  165. // could use readBigInt64LE when support would be 10.20+
  166. const val =
  167. this.buf[this.pos + 4] +
  168. this.buf[this.pos + 5] * 2 ** 8 +
  169. this.buf[this.pos + 6] * 2 ** 16 +
  170. (this.buf[this.pos + 7] << 24);
  171. const vv =
  172. (BigInt(val) << BigInt(32)) +
  173. BigInt(
  174. this.buf[this.pos] +
  175. this.buf[this.pos + 1] * 2 ** 8 +
  176. this.buf[this.pos + 2] * 2 ** 16 +
  177. this.buf[this.pos + 3] * 2 ** 24
  178. );
  179. this.pos += 8;
  180. return vv;
  181. }
  182. readUnsignedLength() {
  183. const type = this.buf[this.pos++] & 0xff;
  184. switch (type) {
  185. case 0xfb:
  186. return null;
  187. case 0xfc:
  188. return this.readUInt16();
  189. case 0xfd:
  190. return this.readUInt24();
  191. case 0xfe:
  192. // limitation to BigInt signed value
  193. return Number(this.readInt64());
  194. default:
  195. return type;
  196. }
  197. }
  198. readBuffer(len) {
  199. this.pos += len;
  200. return this.buf.slice(this.pos - len, this.pos);
  201. }
  202. readBufferRemaining() {
  203. let b = this.buf.slice(this.pos, this.end);
  204. this.pos = this.end;
  205. return b;
  206. }
  207. readBufferLengthEncoded() {
  208. const len = this.readUnsignedLength();
  209. if (len === null) return null;
  210. this.pos += len;
  211. return this.buf.slice(this.pos - len, this.pos);
  212. }
  213. readStringNullEnded() {
  214. let initialPosition = this.pos;
  215. let cnt = 0;
  216. while (this.remaining() > 0 && this.buf[this.pos++] !== 0) {
  217. cnt++;
  218. }
  219. return this.buf.toString('utf8', initialPosition, initialPosition + cnt);
  220. }
  221. readSignedLength() {
  222. const type = this.buf[this.pos++];
  223. switch (type) {
  224. case 0xfb:
  225. return null;
  226. case 0xfc:
  227. return this.readUInt16();
  228. case 0xfd:
  229. return this.readUInt24();
  230. case 0xfe:
  231. return Number(this.readInt64());
  232. default:
  233. return type;
  234. }
  235. }
  236. readSignedLengthBigInt() {
  237. const type = this.buf[this.pos++];
  238. switch (type) {
  239. case 0xfb:
  240. return null;
  241. case 0xfc:
  242. return BigInt(this.readUInt16());
  243. case 0xfd:
  244. return BigInt(this.readUInt24());
  245. case 0xfe:
  246. return this.readInt64();
  247. default:
  248. return BigInt(type);
  249. }
  250. }
  251. readAsciiStringLengthEncoded() {
  252. const len = this.readUnsignedLength();
  253. if (len === null) return null;
  254. this.pos += len;
  255. return this.buf.toString('ascii', this.pos - len, this.pos);
  256. }
  257. readStringLengthEncoded(encoding) {
  258. const len = this.readUnsignedLength();
  259. if (len === null) return null;
  260. this.pos += len;
  261. if (Buffer.isEncoding(encoding)) {
  262. return this.buf.toString(encoding, this.pos - len, this.pos);
  263. }
  264. return Iconv.decode(this.buf.slice(this.pos - len, this.pos), encoding);
  265. }
  266. readLongLengthEncoded(supportBigInt, supportBigNumbers, bigNumberStrings, unsigned) {
  267. const len = this.readUnsignedLength();
  268. if (len === null) return null;
  269. if (supportBigInt) {
  270. const str = this.buf.toString('ascii', this.pos, this.pos + len);
  271. this.pos += len;
  272. return BigInt(str);
  273. }
  274. let result = 0;
  275. let negate = false;
  276. let begin = this.pos;
  277. //minus sign
  278. if (len > 0 && this.buf[begin] === 45) {
  279. negate = true;
  280. begin++;
  281. }
  282. for (; begin < this.pos + len; begin++) {
  283. result = result * 10 + (this.buf[begin] - 48);
  284. }
  285. let val = negate ? -1 * result : result;
  286. this.pos += len;
  287. if (!Number.isSafeInteger(val)) {
  288. const str = this.buf.toString('ascii', this.pos - len, this.pos);
  289. if (bigNumberStrings) return str;
  290. if (supportBigNumbers) {
  291. return Long.fromString(str, unsigned, 10);
  292. }
  293. }
  294. return val;
  295. }
  296. readDecimalLengthEncoded(bigNumberStrings) {
  297. const len = this.readUnsignedLength();
  298. if (len === null) return null;
  299. this.pos += len;
  300. let str = this.buf.toString('ascii', this.pos - len, this.pos);
  301. return bigNumberStrings ? str : +str;
  302. }
  303. readDate() {
  304. const len = this.readUnsignedLength();
  305. if (len === null) return null;
  306. let res = [];
  307. let value = 0;
  308. let initPos = this.pos;
  309. this.pos += len;
  310. while (initPos < this.pos) {
  311. const char = this.buf[initPos++];
  312. if (char === 45) {
  313. //minus separator
  314. res.push(value);
  315. value = 0;
  316. } else {
  317. value = value * 10 + char - 48;
  318. }
  319. }
  320. res.push(value);
  321. //handle zero-date as null
  322. if (res[0] === 0 && res[1] === 0 && res[2] === 0) return null;
  323. return new Date(res[0], res[1] - 1, res[2]);
  324. }
  325. readDateTime(opts) {
  326. const len = this.readUnsignedLength();
  327. if (len === null) return null;
  328. this.pos += len;
  329. const str = this.buf.toString('ascii', this.pos - len, this.pos);
  330. if (str.startsWith('0000-00-00 00:00:00')) return null;
  331. if (opts.tz) {
  332. return new Date(
  333. moment.tz(str, opts.tz).clone().tz(opts.localTz).format('YYYY-MM-DD HH:mm:ss.SSSSSS')
  334. );
  335. }
  336. return new Date(str);
  337. }
  338. readIntLengthEncoded() {
  339. const len = this.readUnsignedLength();
  340. if (len === null) return null;
  341. let result = 0;
  342. let negate = false;
  343. let begin = this.pos;
  344. if (len > 0 && this.buf[begin] === 45) {
  345. //minus sign
  346. negate = true;
  347. begin++;
  348. }
  349. for (; begin < this.pos + len; begin++) {
  350. result = result * 10 + (this.buf[begin] - 48);
  351. }
  352. this.pos += len;
  353. return negate ? -1 * result : result;
  354. }
  355. readFloatLengthCoded() {
  356. const len = this.readUnsignedLength();
  357. if (len === null) return null;
  358. this.pos += len;
  359. return +this.buf.toString('ascii', this.pos - len, this.pos);
  360. }
  361. skipLengthCodedNumber() {
  362. const type = this.buf[this.pos++] & 0xff;
  363. switch (type) {
  364. case 251:
  365. return;
  366. case 252:
  367. this.pos +=
  368. 2 + (0xffff & ((this.buf[this.pos] & 0xff) + ((this.buf[this.pos + 1] & 0xff) << 8)));
  369. return;
  370. case 253:
  371. this.pos +=
  372. 3 +
  373. (0xffffff &
  374. ((this.buf[this.pos] & 0xff) +
  375. ((this.buf[this.pos + 1] & 0xff) << 8) +
  376. ((this.buf[this.pos + 2] & 0xff) << 16)));
  377. return;
  378. case 254:
  379. this.pos +=
  380. 8 +
  381. ((this.buf[this.pos] & 0xff) +
  382. ((this.buf[this.pos + 1] & 0xff) << 8) +
  383. ((this.buf[this.pos + 2] & 0xff) << 16) +
  384. ((this.buf[this.pos + 3] & 0xff) << 24) +
  385. ((this.buf[this.pos + 4] & 0xff) << 32) +
  386. ((this.buf[this.pos + 5] & 0xff) << 40) +
  387. ((this.buf[this.pos + 6] & 0xff) << 48) +
  388. ((this.buf[this.pos + 7] & 0xff) << 56));
  389. return;
  390. default:
  391. this.pos += type;
  392. return;
  393. }
  394. }
  395. positionFromEnd(num) {
  396. this.pos = this.end - num;
  397. }
  398. /**
  399. * For testing purpose only
  400. */
  401. _toBuf() {
  402. return this.buf.slice(this.pos, this.end);
  403. }
  404. forceOffset(off) {
  405. this.pos = off;
  406. }
  407. length() {
  408. return this.end - this.pos;
  409. }
  410. subPacketLengthEncoded() {
  411. const len = this.readUnsignedLength();
  412. this.skip(len);
  413. return new Packet(this.buf, this.pos - len, this.pos);
  414. }
  415. /**
  416. * Parse ERR_Packet : https://mariadb.com/kb/en/library/err_packet/
  417. *
  418. * @param info current connection info
  419. * @param sql command sql
  420. * @param stack additional stack trace
  421. * @returns {Error}
  422. */
  423. readError(info, sql, stack) {
  424. this.skip(1);
  425. let errorCode = this.readUInt16();
  426. let sqlState = '';
  427. if (this.peek() === 0x23) {
  428. this.skip(6);
  429. sqlState = this.buf.toString('utf8', this.pos - 5, this.pos);
  430. }
  431. let msg = this.buf.toString('utf8', this.pos, this.end);
  432. if (sql) msg += '\n' + sql;
  433. let fatal = sqlState.startsWith('08') || sqlState === '70100';
  434. if (fatal) {
  435. const packetMsgs = info.getLastPackets();
  436. if (packetMsgs !== '')
  437. return Errors.createError(
  438. msg + '\nlast received packets:\n' + packetMsgs,
  439. fatal,
  440. info,
  441. sqlState,
  442. errorCode,
  443. stack
  444. );
  445. }
  446. return Errors.createError(msg, fatal, info, sqlState, errorCode, stack);
  447. }
  448. }
  449. module.exports = Packet;