diff --git a/lib/get-certificate.js b/lib/get-certificate.js index 535108d..081b19f 100644 --- a/lib/get-certificate.js +++ b/lib/get-certificate.js @@ -7,7 +7,6 @@ 'use strict'; module.exports.create = function (deps) { - var NOOP=function () {}, log=NOOP; var request=deps.request; var toStandardB64 = deps.leUtils.toStandardB64; var importPemPrivateKey = deps.leCrypto.importPemPrivateKey; @@ -16,6 +15,8 @@ module.exports.create = function (deps) { var Acme = deps.Acme; function getCert(options, cb) { + var NOOP = function () {}; + var log = options.debug ? console.log : NOOP; var state={ validatedDomains:[] , validAuthorizationUrls:[] @@ -56,6 +57,47 @@ module.exports.create = function (deps) { return handleErr(err, 'Failed to parse privateKey'); } + function bodyToError(res, body) { + var err; + + if (!body) { + err = new Error("[Error] letiny-core: no request body"); + err.code = "E_NO_RESPONSE_BODY"; + throw err; + } + + if ('{' === body[0] || '{' === String.fromCharCode(body[0])) { + try { + body = JSON.parse(body); + } catch(e) { + err = new Error("[Error] letiny-core: body could not be parsed"); + err.code = "E_BODY_PARSE"; + err.description = body; + throw err; + } + } + + if (Math.floor(res.statusCode / 100) !== 2) { + err = new Error("[Error] letiny-core: not 200 ok"); + err.code = "E_STATUS_CODE"; + err.type = body.type + err.description = body; + err.detail = body.detail; + throw err; + } + + if (body.type && body.detail) { + err = new Error("[Error] letiny-core: " + body.detail); + err.code = body.type; + err.type = body.type; + err.description = body.detail; + err.detail = body.detail; + throw err; + } + + return body; + } + nextDomain(); function nextDomain() { @@ -98,7 +140,7 @@ module.exports.create = function (deps) { state.authorizationUrl=res.headers.location; state.newCertUrl=links.next; - authz=JSON.parse(body); + authz=body; httpChallenges=authz.challenges.filter(function(x) { return x.type==='http-01'; @@ -136,11 +178,22 @@ module.exports.create = function (deps) { return handleErr(err, 'Authorization status request failed ('+res.statusCode+')'); } - authz=JSON.parse(body); + authz=body; if (authz.status==='pending') { setTimeout(function() { - request.get(state.authorizationUrl, {}, function(err, res, body) { + request({ + method: 'GET' + , url: state.authorizationUrl + }, function(err, res, body) { + if (!err && res.body) { + try { + body = bodyToError(res, body); + } catch(e) { + err = e; + } + } + ensureValidation(err, res, body, unlink); }); }, 1000); @@ -172,12 +225,17 @@ module.exports.create = function (deps) { function downloadCertificate(err, res, body) { var links, certUrl; - if (err || Math.floor(res.statusCode/100)!==2) { - log('Certificate request failed with error ', err); - if (body) { - log(body.toString()); - } - return handleErr(err, 'Certificate request failed'); + if (err) { + handleErr(err, 'Certificate request failed'); + return; + } + + if (Math.floor(res.statusCode/100)!==2) { + err = new Error("invalid status code: " + res.statusCode); + err.code = "E_STATUS_CODE"; + err.description = body; + handleErr(err); + return; } links=Acme.parseLink(res.headers.link); @@ -189,33 +247,54 @@ module.exports.create = function (deps) { state.certificate=body; certUrl=res.headers.location; - request.get({ - url:certUrl, - encoding:null + request({ + method: 'GET' + , url: certUrl + , encoding: null }, function(err, res, body) { + if (!err) { + try { + body = bodyToError(res, body); + } catch(e) { + err = e; + } + } + if (err) { return handleErr(err, 'Failed to fetch cert from '+certUrl); } + if (res.statusCode!==200) { return handleErr(err, 'Failed to fetch cert from '+certUrl, res.body.toString()); } + if (body.toString()!==state.certificate.toString()) { - handleErr(null, 'Cert at '+certUrl+' did not match returned cert'); - } else { - log('Successfully verified cert at '+certUrl); - log('Requesting issuer certificate...'); - request.get({ - url:links.up, - encoding:null - }, function(err, res, body) { - if (err || res.statusCode!==200) { - return handleErr(err, 'Failed to fetch issuer certificate'); - } - state.caCert=certBufferToPem(body); - log('Requesting issuer certificate: done'); - done(); - }); + return handleErr(null, 'Cert at '+certUrl+' did not match returned cert'); } + + log('Successfully verified cert at '+certUrl); + log('Requesting issuer certificate...'); + request({ + method: 'GET' + , url: links.up + , encoding: null + }, function(err, res, body) { + if (!err) { + try { + body = bodyToError(res, body); + } catch(e) { + err = e; + } + } + + if (err || res.statusCode!==200) { + return handleErr(err, 'Failed to fetch issuer certificate'); + } + + state.caCert=certBufferToPem(body); + log('Requesting issuer certificate: done'); + done(); + }); }); }