-
Notifications
You must be signed in to change notification settings - Fork 669
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SFTP: increase max packet length, add missing OpenSSH extensions
- Loading branch information
Showing
3 changed files
with
183 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -154,7 +154,14 @@ class SFTP extends EventEmitter { | |
this._pktData = undefined; | ||
this._writeReqid = -1; | ||
this._requests = {}; | ||
this._maxPktLen = (this._isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000); | ||
this._maxInPktLen = OPENSSH_MAX_PKT_LEN; | ||
this._maxOutPktLen = 34000; | ||
this._maxReadLen = | ||
(this._isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000) - PKT_RW_OVERHEAD; | ||
this._maxWriteLen = | ||
(this._isOpenSSH ? OPENSSH_MAX_PKT_LEN : 34000) - PKT_RW_OVERHEAD; | ||
|
||
this.maxOpenHandles = undefined; | ||
|
||
// Channel compatibility | ||
this._client = client; | ||
|
@@ -208,8 +215,8 @@ class SFTP extends EventEmitter { | |
return; | ||
if (this._pktLen === 0) | ||
return doFatalSFTPError(this, 'Invalid packet length'); | ||
if (this._pktLen > this._maxPktLen) { | ||
const max = this._maxPktLen; | ||
if (this._pktLen > this._maxInPktLen) { | ||
const max = this._maxInPktLen; | ||
return doFatalSFTPError( | ||
this, | ||
`Packet length ${this._pktLen} exceeds max length of ${max}` | ||
|
@@ -432,7 +439,7 @@ class SFTP extends EventEmitter { | |
return; | ||
} | ||
|
||
const maxDataLen = this._maxPktLen - PKT_RW_OVERHEAD; | ||
const maxDataLen = this._maxWriteLen; | ||
const overflow = Math.max(len - maxDataLen, 0); | ||
const origPosition = position; | ||
|
||
|
@@ -1421,7 +1428,7 @@ class SFTP extends EventEmitter { | |
throw new Error('Client-only method called in server mode'); | ||
|
||
const ext = this._extensions['[email protected]']; | ||
if (!ext || ext.indexOf('1') === -1) | ||
if (ext !== '1') | ||
throw new Error('Server does not support this extended request'); | ||
|
||
/* | ||
|
@@ -1461,7 +1468,7 @@ class SFTP extends EventEmitter { | |
throw new Error('Client-only method called in server mode'); | ||
|
||
const ext = this._extensions['[email protected]']; | ||
if (!ext || ext.indexOf('1') === -1) | ||
if (ext !== '1') | ||
throw new Error('Server does not support this extended request'); | ||
if (!Buffer.isBuffer(handle)) | ||
throw new Error('handle is not a Buffer'); | ||
|
@@ -1492,6 +1499,103 @@ class SFTP extends EventEmitter { | |
`SFTP: Outbound: ${isBuffered ? 'Buffered' : 'Sending'} [email protected]` | ||
); | ||
} | ||
ext_openssh_lsetstat(path, attrs, cb) { | ||
if (this.server) | ||
throw new Error('Client-only method called in server mode'); | ||
|
||
const ext = this._extensions['[email protected]']; | ||
if (ext !== '1') | ||
throw new Error('Server does not support this extended request'); | ||
|
||
let flags = 0; | ||
let attrsLen = 0; | ||
|
||
if (typeof attrs === 'object' && attrs !== null) { | ||
attrs = attrsToBytes(attrs); | ||
flags = attrs.flags; | ||
attrsLen = attrs.nb; | ||
} else if (typeof attrs === 'function') { | ||
cb = attrs; | ||
} | ||
|
||
/* | ||
uint32 id | ||
string "[email protected]" | ||
string path | ||
ATTRS attrs | ||
*/ | ||
const pathLen = Buffer.byteLength(path); | ||
let p = 9; | ||
const buf = | ||
Buffer.allocUnsafe(4 + 1 + 4 + 4 + 20 + 4 + pathLen + 4 + attrsLen); | ||
|
||
writeUInt32BE(buf, buf.length - 4, 0); | ||
buf[4] = REQUEST.EXTENDED; | ||
const reqid = this._writeReqid = (this._writeReqid + 1) & MAX_REQID; | ||
writeUInt32BE(buf, reqid, 5); | ||
|
||
writeUInt32BE(buf, 20, p); | ||
buf.utf8Write('[email protected]', p += 4, 20); | ||
|
||
writeUInt32BE(buf, pathLen, p += 20); | ||
buf.utf8Write(path, p += 4, pathLen); | ||
|
||
writeUInt32BE(buf, flags, p += pathLen); | ||
if (attrsLen) { | ||
p += 4; | ||
|
||
if (attrsLen === ATTRS_BUF.length) | ||
buf.set(ATTRS_BUF, p); | ||
else | ||
bufferCopy(ATTRS_BUF, buf, 0, attrsLen, p); | ||
|
||
p += attrsLen; | ||
} | ||
|
||
this._requests[reqid] = { cb }; | ||
|
||
const isBuffered = sendOrBuffer(this, buf); | ||
if (this._debug) { | ||
const status = (isBuffered ? 'Buffered' : 'Sending'); | ||
this._debug(`SFTP: Outbound: ${status} [email protected]`); | ||
} | ||
} | ||
ext_openssh_expandPath(path, cb) { | ||
if (this.server) | ||
throw new Error('Client-only method called in server mode'); | ||
|
||
const ext = this._extensions['[email protected]']; | ||
if (ext !== '1') | ||
throw new Error('Server does not support this extended request'); | ||
|
||
/* | ||
uint32 id | ||
string "[email protected]" | ||
string path | ||
*/ | ||
const pathLen = Buffer.byteLength(path); | ||
let p = 9; | ||
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 23 + 4 + pathLen); | ||
|
||
writeUInt32BE(buf, buf.length - 4, 0); | ||
buf[4] = REQUEST.EXTENDED; | ||
const reqid = this._writeReqid = (this._writeReqid + 1) & MAX_REQID; | ||
writeUInt32BE(buf, reqid, 5); | ||
|
||
writeUInt32BE(buf, 23, p); | ||
buf.utf8Write('[email protected]', p += 4, 23); | ||
|
||
writeUInt32BE(buf, pathLen, p += 20); | ||
buf.utf8Write(path, p += 4, pathLen); | ||
|
||
this._requests[reqid] = { cb }; | ||
|
||
const isBuffered = sendOrBuffer(this, buf); | ||
if (this._debug) { | ||
const status = (isBuffered ? 'Buffered' : 'Sending'); | ||
this._debug(`SFTP: Outbound: ${status} [email protected]`); | ||
} | ||
} | ||
// =========================================================================== | ||
// Server-specific =========================================================== | ||
// =========================================================================== | ||
|
@@ -1760,7 +1864,7 @@ function tryCreateBuffer(size) { | |
} | ||
|
||
function read_(self, handle, buf, off, len, position, cb, req_) { | ||
const maxDataLen = self._maxPktLen - PKT_RW_OVERHEAD; | ||
const maxDataLen = self._maxReadLen; | ||
const overflow = Math.max(len - maxDataLen, 0); | ||
|
||
if (overflow) | ||
|
@@ -2394,6 +2498,31 @@ function cleanupRequests(sftp) { | |
} | ||
} | ||
|
||
function requestLimits(sftp, cb) { | ||
/* | ||
uint32 id | ||
string "[email protected]" | ||
*/ | ||
let p = 9; | ||
const buf = Buffer.allocUnsafe(4 + 1 + 4 + 4 + 18); | ||
|
||
writeUInt32BE(buf, buf.length - 4, 0); | ||
buf[4] = REQUEST.EXTENDED; | ||
const reqid = sftp._writeReqid = (sftp._writeReqid + 1) & MAX_REQID; | ||
writeUInt32BE(buf, reqid, 5); | ||
|
||
writeUInt32BE(buf, 18, p); | ||
buf.utf8Write('[email protected]', p += 4, 18); | ||
|
||
sftp._requests[reqid] = { extended: '[email protected]', cb }; | ||
|
||
const isBuffered = sendOrBuffer(sftp, buf); | ||
if (sftp._debug) { | ||
const which = (isBuffered ? 'Buffered' : 'Sending'); | ||
sftp._debug(`SFTP: Outbound: ${which} [email protected]`); | ||
} | ||
} | ||
|
||
const CLIENT_HANDLERS = { | ||
[RESPONSE.VERSION]: (sftp, payload) => { | ||
if (sftp._version !== -1) | ||
|
@@ -2434,6 +2563,24 @@ const CLIENT_HANDLERS = { | |
|
||
sftp._version = version; | ||
sftp._extensions = extensions; | ||
|
||
if (extensions['[email protected]'] === '1') { | ||
return requestLimits(sftp, (err, limits) => { | ||
if (!err) { | ||
if (limits.maxPktLen > 0) | ||
sftp._maxOutPktLen = limits.maxPktLen; | ||
if (limits.maxReadLen > 0) | ||
sftp._maxReadLen = limits.maxReadLen; | ||
if (limits.maxWriteLen > 0) | ||
sftp._maxWriteLen = limits.maxWriteLen; | ||
sftp.maxOpenHandles = ( | ||
limits.maxOpenHandles > 0 ? limits.maxOpenHandles : Infinity | ||
); | ||
} | ||
sftp.emit('ready'); | ||
}); | ||
} | ||
|
||
sftp.emit('ready'); | ||
}, | ||
[RESPONSE.STATUS]: (sftp, payload) => { | ||
|
@@ -2669,6 +2816,32 @@ const CLIENT_HANDLERS = { | |
req.cb(undefined, stats); | ||
return; | ||
} | ||
case '[email protected]': { | ||
/* | ||
uint64 max-packet-length | ||
uint64 max-read-length | ||
uint64 max-write-length | ||
uint64 max-open-handles | ||
*/ | ||
const limits = { | ||
maxPktLen: bufferParser.readUInt64BE(), | ||
maxReadLen: bufferParser.readUInt64BE(), | ||
maxWriteLen: bufferParser.readUInt64BE(), | ||
maxOpenHandles: bufferParser.readUInt64BE(), | ||
}; | ||
if (limits.maxOpenHandles === undefined) | ||
break; | ||
if (sftp._debug) { | ||
sftp._debug( | ||
'SFTP: Inbound: Received EXTENDED_REPLY ' | ||
+ `(id:${reqID}, ${req.extended})` | ||
); | ||
} | ||
bufferParser.clear(); | ||
if (typeof req.cb === 'function') | ||
req.cb(undefined, limits); | ||
return; | ||
} | ||
default: | ||
// Unknown extended request | ||
sftp._debug && sftp._debug( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters