3 var Sntp = require('sntp');
\r
4 var Boom = require('boom');
\r
12 exports.version = function () {
\r
14 return require('../package.json').version;
\r
19 maxMatchLength: 4096 // Limit the length of uris and headers to avoid a DoS attack on string matching
\r
23 // Extract host and port from request
\r
26 internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/; // (IPv4, hostname)|(IPv6)
\r
29 exports.parseHost = function (req, hostHeaderName) {
\r
31 hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
\r
32 var hostHeader = req.headers[hostHeaderName];
\r
37 if (hostHeader.length > exports.limits.maxMatchLength) {
\r
41 var hostParts = hostHeader.match(internals.hostHeaderRegex);
\r
48 port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
\r
53 // Parse Content-Type header content
\r
55 exports.parseContentType = function (header) {
\r
61 return header.split(';')[0].trim().toLowerCase();
\r
65 // Convert node's to request configuration object
\r
67 exports.parseRequest = function (req, options) {
\r
73 // Obtain host and port information
\r
76 if (!options.host ||
\r
79 host = exports.parseHost(req, options.hostHeaderName);
\r
81 return new Error('Invalid Host header');
\r
88 host: options.host || host.name,
\r
89 port: options.port || host.port,
\r
90 authorization: req.headers.authorization,
\r
91 contentType: req.headers['content-type'] || ''
\r
98 exports.now = function (localtimeOffsetMsec) {
\r
100 return Sntp.now() + (localtimeOffsetMsec || 0);
\r
104 exports.nowSecs = function (localtimeOffsetMsec) {
\r
106 return Math.floor(exports.now(localtimeOffsetMsec) / 1000);
\r
110 internals.authHeaderRegex = /^(\w+)(?:\s+(.*))?$/; // Header: scheme[ something]
\r
111 internals.attributeRegex = /^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/; // !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9
\r
114 // Parse Hawk HTTP Authorization header
\r
116 exports.parseAuthorizationHeader = function (header, keys) {
\r
118 keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];
\r
121 return Boom.unauthorized(null, 'Hawk');
\r
124 if (header.length > exports.limits.maxMatchLength) {
\r
125 return Boom.badRequest('Header length too long');
\r
128 var headerParts = header.match(internals.authHeaderRegex);
\r
129 if (!headerParts) {
\r
130 return Boom.badRequest('Invalid header syntax');
\r
133 var scheme = headerParts[1];
\r
134 if (scheme.toLowerCase() !== 'hawk') {
\r
135 return Boom.unauthorized(null, 'Hawk');
\r
138 var attributesString = headerParts[2];
\r
139 if (!attributesString) {
\r
140 return Boom.badRequest('Invalid header syntax');
\r
143 var attributes = {};
\r
144 var errorMessage = '';
\r
145 var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {
\r
147 // Check valid attribute names
\r
149 if (keys.indexOf($1) === -1) {
\r
150 errorMessage = 'Unknown attribute: ' + $1;
\r
154 // Allowed attribute value characters
\r
156 if ($2.match(internals.attributeRegex) === null) {
\r
157 errorMessage = 'Bad attribute value: ' + $1;
\r
161 // Check for duplicates
\r
163 if (attributes.hasOwnProperty($1)) {
\r
164 errorMessage = 'Duplicate attribute: ' + $1;
\r
168 attributes[$1] = $2;
\r
172 if (verify !== '') {
\r
173 return Boom.badRequest(errorMessage || 'Bad header format');
\r
180 exports.unauthorized = function (message, attributes) {
\r
182 return Boom.unauthorized(message, 'Hawk', attributes);
\r