From c99bdfe74799f3e4bd8c118987da201f49d6d370 Mon Sep 17 00:00:00 2001 From: "Mahlon E. Smith" Date: Fri, 12 May 2017 16:17:41 -0700 Subject: [PATCH] Migrate hashing functions to C. --- .hgignore | 2 +- README.md | 14 ++- Rakefile | 18 +++- experiments/surf.pdf | Bin 0 -> 133405 bytes ext/ezmlm/hash/extconf.rb | 5 + ext/ezmlm/hash/hash.c | 193 ++++++++++++++++++++++++++++++++++++++ ext/ezmlm/hash/hash.h | 46 +++++++++ lib/ezmlm.rb | 1 + lib/ezmlm/list.rb | 42 ++------- spec/ezmlm/list_spec.rb | 5 + 10 files changed, 287 insertions(+), 39 deletions(-) create mode 100644 experiments/surf.pdf create mode 100644 ext/ezmlm/hash/extconf.rb create mode 100644 ext/ezmlm/hash/hash.c create mode 100644 ext/ezmlm/hash/hash.h diff --git a/.hgignore b/.hgignore index 5ea3223..409dac2 100644 --- a/.hgignore +++ b/.hgignore @@ -1,4 +1,4 @@ Session.vim pkg/* docs/* - +tmp/* diff --git a/README.md b/README.md index 7d00f82..08dc394 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ This was tested against ezmlm-idx 7.2.2. *Strong recommendation*: Create your lists with archiving (-a) and indexing (-i)! This library is suitable for modifying behavior of existing lists as a default, but with these flags enabled, can also -be an interface to parsing and browsing the content of lists. +be a generic interface for parsing and browsing list content. ## Prerequisites @@ -67,6 +67,18 @@ page for ezmlm-reject.) Patches are welcome if you'd like these sorts of miscellaneous things included. +## Acknowledgments + +Portions of this library are copied from ezmlm-idx source, authored by +the following: + + * D. J. Bernstein + * Bruce Guenter + +Many thanks for Dan and Bruce for their commitment for fine software, and +contributions to the internet communities over the years. + + ## License Copyright (c) 2017, Mahlon E. Smith diff --git a/Rakefile b/Rakefile index 80fde89..e6d612f 100644 --- a/Rakefile +++ b/Rakefile @@ -3,6 +3,12 @@ require 'pathname' +begin + require 'rake/extensiontask' +rescue LoadError + abort "This Rakefile requires rake-compiler (gem install rake-compiler)" +end + PROJECT = 'ezmlm' BASEDIR = Pathname.new( __FILE__ ).expand_path.dirname.relative_path_from( Pathname.getwd ) LIBDIR = BASEDIR + 'lib' @@ -49,6 +55,7 @@ manager for use with the Qmail MTA, and the messages contained therein. (The -idx provides an extended feature set over the original ezmlm environment.) EOF + s.extensions = FileList[ "ext/**/extconf.rb" ] s.required_ruby_version = '>= 2.1' s.add_dependency 'mail', "~> 2.6" @@ -60,6 +67,9 @@ Gem::PackageTask.new( spec ) do |pkg| pkg.need_tar = true end +# Build the C extension for hashing addresses. +Rake::ExtensionTask.new( 'ezmlm/hash', spec ) + ######################################################################## ### D O C U M E N T A T I O N @@ -74,7 +84,7 @@ begin rdoc.rdoc_dir = 'docs' rdoc.main = "README.rdoc" # rdoc.options = [ '-f', 'fivefish' ] - rdoc.rdoc_files = [ 'lib', *FileList['*.rdoc'] ] + rdoc.rdoc_files = [ 'lib', *FileList['ext/*/*.c'], *FileList['*.rdoc'] ] end RDoc::Task.new do |rdoc| @@ -116,6 +126,12 @@ end ### M A N I F E S T ######################################################################## __END__ +ext/ezmlm/hash/hash.c +ext/ezmlm/hash/hash.h +ext/ezmlm/hash/extconf.rb lib/ezmlm/list.rb +lib/ezmlm/list/message.rb +lib/ezmlm/list/thread.rb +lib/ezmlm/list/author.rb lib/ezmlm.rb diff --git a/experiments/surf.pdf b/experiments/surf.pdf new file mode 100644 index 0000000000000000000000000000000000000000..66ce026cd44a69bae62a4b849faf3f766c3a633f GIT binary patch literal 133405 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 literal 0 HcmV?d00001 diff --git a/ext/ezmlm/hash/extconf.rb b/ext/ezmlm/hash/extconf.rb new file mode 100644 index 0000000..0c4d4b2 --- /dev/null +++ b/ext/ezmlm/hash/extconf.rb @@ -0,0 +1,5 @@ + +require 'mkmf' + +create_makefile( 'ezmlm/hash' ) + diff --git a/ext/ezmlm/hash/hash.c b/ext/ezmlm/hash/hash.c new file mode 100644 index 0000000..7c913b8 --- /dev/null +++ b/ext/ezmlm/hash/hash.c @@ -0,0 +1,193 @@ + +#include "hash.h" + +/* + * I originally attemped to just convert surf.c to pure Ruby, but I + * confess a lack of understanding surrounding the char casts from + * unsigned ints, etc, and screwing up a hash algo doesn't do anyone + * any good, least of all, me. In other words, I don't have to fully + * understand DJB code to trust in it. :-) + * + * The following is copied verbatim from the ezmlm-idx source, version + * 7.2.2. See: subhash.c, surf.c, and surfpcs.c. + * +*/ + +void surf(unsigned int out[8],const unsigned int in[12],const unsigned int seed[32]) +{ + unsigned int t[12]; unsigned int x; unsigned int sum = 0; + int r; int i; int loop; + + for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i]; + for (i = 0;i < 8;++i) out[i] = seed[24 + i]; + x = t[11]; + for (loop = 0;loop < 2;++loop) { + for (r = 0;r < 16;++r) { + sum += 0x9e3779b9; + MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) + MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) + MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) + } + for (i = 0;i < 8;++i) out[i] ^= t[i + 4]; + } +} + +void surfpcs_init(surfpcs *s,const unsigned int k[32]) +{ + int i; + for (i = 0;i < 32;++i) s->seed[i] = k[i]; + for (i = 0;i < 8;++i) s->sum[i] = 0; + for (i = 0;i < 12;++i) s->in[i] = 0; + s->todo = 0; +} + +void surfpcs_add(surfpcs *s,const char *x,unsigned int n) +{ + int i; + while (n--) { + data[end[s->todo++]] = *x++; + if (s->todo == 32) { + s->todo = 0; + if (!++s->in[8]) + if (!++s->in[9]) + if (!++s->in[10]) + ++s->in[11]; + surf(s->out,s->in,s->seed); + for (i = 0;i < 8;++i) + s->sum[i] += s->out[i]; + } + } +} + +void surfpcs_addlc(surfpcs *s,const char *x,unsigned int n) +/* modified from surfpcs_add by case-independence and skipping ' ' & '\t' */ +{ + unsigned char ch; + int i; + while (n--) { + ch = *x++; + if (ch == ' ' || ch == '\t') continue; + if (ch >= 'A' && ch <= 'Z') + ch -= 'a' - 'A'; + + data[end[s->todo++]] = ch; + if (s->todo == 32) { + s->todo = 0; + if (!++s->in[8]) + if (!++s->in[9]) + if (!++s->in[10]) + ++s->in[11]; + surf(s->out,s->in,s->seed); + for (i = 0;i < 8;++i) + s->sum[i] += s->out[i]; + } + } +} + +void surfpcs_out(surfpcs *s,unsigned char h[32]) +{ + int i; + surfpcs_add(s,".",1); + while (s->todo) surfpcs_add(s,"",1); + for (i = 0;i < 8;++i) s->in[i] = s->sum[i]; + for (;i < 12;++i) s->in[i] = 0; + surf(s->out,s->in,s->seed); + for (i = 0;i < 32;++i) h[i] = outdata[end[i]]; +} + +void makehash(const char *indata,unsigned int inlen,char *hash) + /* makes hash[COOKIE=20] from stralloc *indata, ignoring case and */ + /* SPACE/TAB */ +{ + unsigned char h[32]; + surfpcs s; + unsigned int seed[32]; + int i; + + for (i = 0;i < 32;++i) seed[i] = 0; + surfpcs_init(&s,seed); + surfpcs_addlc(&s,indata,inlen); + surfpcs_out(&s,h); + for (i = 0;i < 20;++i) + hash[i] = 'a' + (h[i] & 15); +} + +unsigned int subhashb(const char *s,long len) +{ + unsigned long h; + h = 5381; + while (len-- > 0) + h = (h + (h << 5)) ^ (unsigned int)*s++; + return h % 53; +} + +unsigned int subhashs(const char *s) +{ + return subhashb(s,strlen(s)); +} + +/* end copy of ezmlm-idx source */ + + + + +/* + * call­seq: + * Ezmlm::Hash.address( email ) -> String + * + * Call the Surf hashing function on an +email+ address, returning + * the hashed string. This is specific to how ezmlm is seeding + * the hash, and parsing email addresses from messages (prefixed with + * the '<' character.) + * + */ +VALUE +address( VALUE klass, VALUE email ) { + char hash[20]; + char *input; + + Check_Type( email, T_STRING ); + + email = rb_str_plus( rb_str_new2("<"), email); + input = StringValueCStr( email ); + + makehash( input, strlen(input), hash ); + + return rb_str_new2( hash ); +} + + +/* + * call­seq: + * Ezmlm::Hash.subscriber( address ) -> String + * + * Call the subscriber hashing function on an email +address+, returning + * the index character referring to the file containing subscriber presence. + * + */ +VALUE +subscriber( VALUE klass, VALUE email ) { + unsigned int prefix; + + Check_Type( email, T_STRING ); + + email = rb_str_plus( rb_str_new2("T"), email); + prefix = subhashs( StringValueCStr(email) ) + 64; + + return rb_sprintf( "%c", (char)prefix ); +} + + + +void +Init_hash() +{ + rb_mEzmlm = rb_define_module( "Ezmlm" ); + rb_cEZHash = rb_define_class_under( rb_mEzmlm, "Hash", rb_cObject ); + + rb_define_module_function( rb_cEZHash, "address", address, 1 ); + rb_define_module_function( rb_cEZHash, "subscriber", subscriber, 1 ); + + return; +} + diff --git a/ext/ezmlm/hash/hash.h b/ext/ezmlm/hash/hash.h new file mode 100644 index 0000000..9b4c15e --- /dev/null +++ b/ext/ezmlm/hash/hash.h @@ -0,0 +1,46 @@ + +#include + +static VALUE rb_mEzmlm; +static VALUE rb_cEZHash; + + +#ifndef SURF_H +#define SURF_H + +#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) +#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); + +typedef struct { + unsigned int seed[32]; + unsigned int sum[8]; + unsigned int out[8]; + unsigned int in[12]; + int todo; +} surfpcs; + +static const unsigned int littleendian[8] = { + 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, + 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c +} ; +#define end ((unsigned char *) &littleendian) +#define data ((unsigned char *) s->in) +#define outdata ((unsigned char *) s->out) + +extern void surf( unsigned int out[8], const unsigned int in[12], const unsigned int seed[32] ); +extern void surfpcs_init( surfpcs *s, const unsigned int k[32] ); +extern void surfpcs_add( surfpcs *s, const char *x,unsigned int n ); +extern void surfpcs_addlc( surfpcs *s, const char *x,unsigned int n ); +extern void surfpcs_out( surfpcs *s, unsigned char h[32] ); +#endif + + +#ifndef SUBHASH_H +#define SUBHASH_H + +unsigned int subhashs(const char *s); +unsigned int subhashb(const char *s,long len); +#define subhashsa(SA) subhashb((SA)->s,(SA)->len) + +#endif + diff --git a/lib/ezmlm.rb b/lib/ezmlm.rb index f9c3180..e5848f5 100644 --- a/lib/ezmlm.rb +++ b/lib/ezmlm.rb @@ -23,6 +23,7 @@ module Ezmlm # Suck in the components. # + require 'ezmlm/hash' require 'ezmlm/list' require 'ezmlm/list/author' require 'ezmlm/list/message' diff --git a/lib/ezmlm/list.rb b/lib/ezmlm/list.rb index 800eb85..ece10a3 100644 --- a/lib/ezmlm/list.rb +++ b/lib/ezmlm/list.rb @@ -19,10 +19,6 @@ require 'ezmlm' unless defined?( Ezmlm ) ### class Ezmlm::List - # Quick address space detection, to (hopefully) - # match the overflow size on this machine. - ADDRESS_SPACE = [ 'i' ].pack( 'p' ).size * 8 - # Valid subdirectories/sections for subscriptions. SUBSCRIPTION_DIRS = %w[ deny mod digest allow ] @@ -75,7 +71,7 @@ class Ezmlm::List ### def include?( addr, section: nil ) addr.downcase! - file = self.subscription_dir( section ) + self.hashchar( addr ) + file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( addr ) return false unless file.exist? return file.read.scan( /T([^\0]+)\0/ ).flatten.include?( addr ) end @@ -97,7 +93,7 @@ class Ezmlm::List next unless address.index( '@' ) address.downcase! - file = self.subscription_dir( section ) + self.hashchar( address ) + file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address ) self.with_safety do if file.exist? addresses = file.read.scan( /T([^\0]+)\0/ ).flatten @@ -123,7 +119,7 @@ class Ezmlm::List addr.each do |address| address.downcase! - file = self.subscription_dir( section ) + self.hashchar( address ) + file = self.subscription_dir( section ) + Ezmlm::Hash.subscriber( address ) self.with_safety do next unless file.exist? addresses = file.read.scan( /T([^\0]+)\0/ ).flatten @@ -680,10 +676,12 @@ class Ezmlm::List end - ### Return an Author object for the given +author_id+. + ### Return an Author object for the given +author_id+, which + ### could also be an email address. ### def author( author_id ) raise "Archiving is not enabled." unless self.archived? + author_id = Ezmlm::Hash.address(author_id) if author_id.index( '@' ) return Ezmlm::List::Author.new( self, author_id ) end @@ -729,34 +727,6 @@ class Ezmlm::List protected ######### - ### Hash an email address, using the ezmlm algorithm for - ### fast user lookups. Returns the hashed integer. - ### - ### Older ezmlm didn't lowercase addresses, anything within the last - ### decade did. We're not going to worry about compatibility there. - ### - ### See: subhash.c in the ezmlm source. - ### - def subhash( addr ) - h = 5381 - over = 2 ** ADDRESS_SPACE - - addr = 'T' + addr.downcase - addr.each_char do |c| - h = ( h + ( h << 5 ) ) ^ c.ord - h = h % over if h > over # emulate integer overflow - end - return h % 53 - end - - - ### Given an email address, return the ascii hash prefix. - ### - def hashchar( addr ) - return ( self.subhash(addr) + 64 ).chr - end - - ### Just return the contents of the provided +file+, rooted ### in the list directory. ### diff --git a/spec/ezmlm/list_spec.rb b/spec/ezmlm/list_spec.rb index 19e437b..bf0e443 100644 --- a/spec/ezmlm/list_spec.rb +++ b/spec/ezmlm/list_spec.rb @@ -341,6 +341,11 @@ describe Ezmlm::List do expect( list.author('ojjhjlapnejjlbcplabi') ).to be_a( Ezmlm::List::Author ) end + it 'fetches author objects by email address' do + author = list.author( 'ojjhjlapnejjlbcplabi' ) + expect( list.author('yvette@example.net').name ).to eq( author.name ) + end + context 'fetching messages' do it 'raises an error if archiving is disabled' do