From 9dd0b9afd235e1bf94b9b148a85a972dec9d4fed Mon Sep 17 00:00:00 2001 From: Andrew Van Tassel Date: Mon, 15 Jun 2015 13:33:52 -0600 Subject: [PATCH] Added header parsing --- CHANGELOG.md | 4 +-- README.md | 7 +++- lib/api.js | 81 ++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- test/header-test.eml | 48 ++++++++++++++++++++++++++ test/mailhops.js | 4 ++- test/main.js | 19 +++++++++-- 7 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 test/header-test.eml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bf1712..9c93135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Change Log All notable changes to this project will be documented in this file. -## [Unreleased][unreleased] +## [0.0.3] - 2015-06-15 ### Added -- Header parsing +- Header IP parsing ## [0.0.1] - 2015-06-14 diff --git a/README.md b/README.md index 5cb98de..de55653 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,13 @@ var options = { } mailhops.configure(options); +// get IPs from a full header where headerText is the full header +var ips = mailhops.getIPsFromHeader(headerText); -mailhops.lookup(['216.58.217.46','98.138.253.109'],function(err,response){ +// or pass in an array of IP addresses +var ips = ['216.58.217.46','98.138.253.109']; + +mailhops.lookup(ips,function(err,response){ console.log(response); }); diff --git a/lib/api.js b/lib/api.js index 90e0b58..ceffde8 100644 --- a/lib/api.js +++ b/lib/api.js @@ -39,5 +39,86 @@ module.exports = { qs.fkey = this.forecastio_api_key; return [this.base_uri, this.api_version, "map", '?'+querystring.stringify(qs)].join("/"); + }, + + //parse the whole email header + getIPsFromHeader: function(header){ + var receivedHeaders = this.getReceivedHeaders(header) + ,ips = [] + ,regexIp=/(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}$/ + ,regexAllIp = /(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)\.(1\d{0,2}|2(?:[0-4]\d{0,1}|[6789]|5[0-5]?)?|[3-9]\d?|0)(\/(?:[012]\d?|3[012]?|[456789])){0,1}/g + ,regexIPV6 = /(::|(([a-fA-F0-9]{1,4}):){7}(([a-fA-F0-9]{1,4}))|(:(:([a-fA-F0-9]{1,4})){1,6})|((([a-fA-F0-9]{1,4}):){1,6}:)|((([a-fA-F0-9]{1,4}):)(:([a-fA-F0-9]{1,4})){1,6})|((([a-fA-F0-9]{1,4}):){2}(:([a-fA-F0-9]{1,4})){1,5})|((([a-fA-F0-9]{1,4}):){3}(:([a-fA-F0-9]{1,4})){1,4})|((([a-fA-F0-9]{1,4}):){4}(:([a-fA-F0-9]{1,4})){1,3})|((([a-fA-F0-9]{1,4}):){5}(:([a-fA-F0-9]{1,4})){1,2}))/; + + _.each(receivedHeaders, function(line){ + + //IPV6 check + if(line.match(regexIPV6)){ + ips.push( line.match(regexIPV6)[0] ); + return; + } + + var received_ips = line.match(regexAllIp); + + //maybe multiple IPs in one Received: line + _.each(received_ips, function(ip){ + + var firstchar = line.substring(line.indexOf(ip)-1).substring(0,1); + var lastchar = line.substring(line.indexOf(ip)+ip.length).substring(0,1); + + //do some checks on the first and last characters surrounding the IP + // Microsoft SMTP Server id 14.3.195.1; something like this should not be included + if(!firstchar.match(/\.|\d|\-/) + && !lastchar.match(/\.|\d|\-/) + && ( firstchar != '?' && lastchar != '?' ) + && lastchar != ';' + && regexIp.test(ip) + && ips.indexOf(ip)===-1){ + + ips.push( ip ); + + } + }); + }); + + return ips; + }, + + getReceivedHeaders: function(header){ + var receivedHeaders = []; + var rline = ''; + + if ( header ){ + var headers = header.split("\n"); + _.each(headers,function(line){ + + //if the header line begins with Received, X-Received or X-Originating-IP then push that to our array + if(line.indexOf('Received:')===0 || line.indexOf('X-Received:')===0){ + if(rline != ''){ + receivedHeaders.push(rline); + rline = ''; + rline += line; + return; + } else { + rline += line; + } + } else if(line.indexOf('X-Originating-IP:')===0){ + receivedHeaders.push(line); + return; + } else if(rline != ''){ + //keep concatting the receive header until we find a semicolon + if(line.indexOf(';')===-1){ + rline += line; + } else { + rline += line; + receivedHeaders.push(rline); + rline = ''; + return; + } + } + }); + if(rline != '') + receivedHeaders.push(rline); + } + return receivedHeaders; } } \ No newline at end of file diff --git a/package.json b/package.json index 8203703..0ced080 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mailhops", - "version": "0.0.2", + "version": "0.0.3", "description": "A nodejs module for interacting with the MailHops API.", "main": "main.js", "dependencies": { diff --git a/test/header-test.eml b/test/header-test.eml new file mode 100644 index 0000000..7bfbe46 --- /dev/null +++ b/test/header-test.eml @@ -0,0 +1,48 @@ +Return-Path: +X-Originating-IP: [2607:fb90:50f:5547:0:46:e46a:bd01] +X-Original-To: andrew@mailhops.com +Delivered-To: x12669597@homiemail-mx32.g.dreamhost.com +Received: from o2.delivery2.klaviyomail.com (o2.delivery2.klaviyomail.com [198.21.5.108]) + (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) + (No client certificate requested) + by homiemail-mx32.g.dreamhost.com (Postfix) with ESMTPS id 4BE0510190916 + for ; Mon, 15 Jun 2015 10:38:03 -0700 (PDT) +DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; + d=delivery.klaviyomail.com; + h=content-type:mime-version:from:to:subject; s=smtpapi; + bh=lu70JKUbpAjCA9G5JXKZECq8PFc=; b=PNXTO8t5e2Bh/kmKAmOYvgQ+MDGfj + 4DAOoP00xozjkYc6KMkyOLFUrfRrZEpBYXQC0m6IXDsyiTuk8SkopTpcc8YYitoa + yv4eaW1h9XRG0VYvl3RbBX5QFZsikRmReBO9Yu8gdLizZ8X83SF5TCwDLU99V/OL + F6oLELBhbNFezk= +Received: by filter0500p1mdw1.sendgrid.net with SMTP id filter0500p1mdw1.14444.557F0D731E + 2015-06-15 17:37:59.069788226 +0000 UTC +Received: from queue-worker-166.servers.clovesoftware.com (ec2-54-157-138-253.compute-1.amazonaws.com [54.157.138.253]) + by ismtpd-013 (SG) with ESMTP id 14df84c98b9.582.18b0cc + for ; Mon, 15 Jun 2015 17:37:58 +0000 (UTC) +Received: from ip-10-157-90-72.ec2.internal (localhost [127.0.0.1]) + by queue-worker-166.servers.clovesoftware.com (Postfix) with ESMTP id E2343271B8 + for ; Mon, 15 Jun 2015 17:37:58 +0000 (UTC) +Received: from edge01.net.lu.se (130.235.56.196) by uwcas04.uw.lu.se + (130.235.59.236) with Microsoft SMTP Server (TLS) id 14.3.195.1; Thu, 4 Sep + 2014 11:38:30 +0200 +Received: from muon.isy.liu.se (130.236.48.25) by edge01.net.lu.se + (130.235.56.196) with Microsoft SMTP Server id 14.3.195.1; Thu, 4 Sep 2014 + 11:38:29 +0200 +Received: from ip227-36.wireless.lu.se (ip227-36.wireless.lu.se + [130.235.227.36]) (using TLSv1 with cipher AES128-SHA (128/128 bits)) (No + client certificate requested) by muon.isy.liu.se (Postfix) with ESMTPSA id + D4B3D802; Thu, 4 Sep 2014 11:38:28 +0200 (MEST) +Content-Type: multipart/alternative; + boundary="===============2767014682564602329==" +MIME-Version: 1.0 +From: ProgrammableWeb Today +To: Andrew Van Tassel +Subject: Is It Time to Move On from .NET? +Date: Mon, 15 Jun 2015 17:37:58 GMT +X-Kmail-Relay: [gmh6f2.krelaymail.com]:587 +Message-Id: <20150615173758.E2343271B8@queue-worker-166.servers.clovesoftware.com> +X-SG-EID: z/7dFXGDBkx/fUP8kk8C2xB01JCsbu4AgSgValkgiTccFIBxI9L4Y1qe6fbe+Uf2YVSIXGTcm+Mu+n + idt9gFAaQCzrGKYViH5a2BTU5yTlhPKLPdY6IcUKXoofBE4zcfPxZ0mr5orMgWetTx9CAhDgReTGyz + F59uuTcPAGosAUo= +X-SG-ID: x3+8Rw3sG8PRR1BPwp/fGYid6bBXOCegs1g2PMmkkWW51Br61ph7xHEIqgadleR9yav2PSHdWBO49l + vFFxpgDQIK2ywWpGxvmvOx3RiyFUs= diff --git a/test/mailhops.js b/test/mailhops.js index 70a909e..0b0c99d 100644 --- a/test/mailhops.js +++ b/test/mailhops.js @@ -15,7 +15,9 @@ describe("mailhops", function(){ var required_keys = [ "configure", "lookup", - "mapUrl" + "mapUrl", + "getIPsFromHeader", + "getReceivedHeaders" ] assert.deepEqual(_.keys(mailhops.__proto__), required_keys); diff --git a/test/main.js b/test/main.js index c9fab96..44e71bc 100644 --- a/test/main.js +++ b/test/main.js @@ -1,5 +1,6 @@ var _ = require("lodash"); var assert = require("assert"); +var fs = require('fs'); var configuration = require([__dirname, "..", "config"].join("/")); var pkg = require([__dirname, "..", "package"].join("/")); var MailHops = require([__dirname, "..", "main"].join("/")); @@ -30,9 +31,9 @@ describe("main", function(){ }); - describe("GET lookup", function(){ + describe("lookup endpoint", function(){ - it('should return a 200 response with private ip', function(done){ + it('string route should return a 200 response with private ip', function(done){ mailhops.lookup('127.0.0.1', function(err, response){ assert.equal(response.meta['code'],200); assert.equal(response.response.route[0]['private'],true); @@ -40,7 +41,7 @@ describe("main", function(){ }); }); - it('array lookup test should return a 200 response with private ip', function(done){ + it('array route should return a 200 response with private ip', function(done){ mailhops.lookup(['127.0.0.1','216.58.217.46','98.138.253.109'], function(err, response){ assert.equal(response.meta['code'],200); assert.equal(response.response.route[0]['private'],true); @@ -49,4 +50,16 @@ describe("main", function(){ }); }); + describe("parse header", function(){ + it('should return an array of 8 IP addresses', function(done){ + + //read header form file + var header = fs.readFileSync(__dirname+'/header-test.eml',{ encoding: 'utf8' }); + + assert.equal(mailhops.getIPsFromHeader(header).length,8); + + done(); + }); + }); + });