/* This file is in the public domain. */ static const char *userlist = "/etc/mailshim-userlist"; #include #include #include #include #include "validaddr.h" typedef struct user USER; struct user { USER *link; unsigned int hash; int nlen; char name[0]; } ; static int nusers = -1; static int tblsize = 0; static USER **table = 0; static struct stat file_stb; static void free_user_list(USER **lp) { USER *u; while ((u = *lp)) { *lp = u->link; free(u); } } static unsigned int hash_block(const void *data, int len) { unsigned int h; int i; h = len; for (i=0;i> 1) ^ ((h&1) ? 0xedb88320 : 0) ^ (((const unsigned char *)data)[i] | 0x20); return(h); } static void grow_table(void) { int newsize; USER **newtbl; USER *u; int i; int b; newsize = (tblsize?:32) << 1; newtbl = malloc(newsize*sizeof(USER *)); for (i=0;ilink; b = u->hash % newsize; u->link = newtbl[b]; newtbl[b] = u; } } free(table); table = newtbl; tblsize = newsize; } static int reload_users(void) { FILE *f; int ll; USER *u; int bucket; int i; char line[1024]; for (i=0;i 0) && (line[ll-1] == '\n')) line[--ll] = '\0'; u = malloc(sizeof(USER)+ll); u->nlen = ll; bcopy(&line[0],&u->name[0],ll); u->hash = hash_block(&line[0],ll); nusers ++; if (nusers > tblsize) grow_table(); bucket = u->hash % tblsize; u->link = table[bucket]; table[bucket] = u; } if (ferror(f)) { fclose(f); nusers = -1; return(1); } fclose(f); return(0); } int valid_address(const char *lp, const char *dom) { struct stat stb; unsigned int h; USER *u; int ll; /* If there's no domain part, it must be a bare . */ if (dom == 0) return(VA_GOOD); /* The domain must be Example. */ if (strcasecmp(dom,"example.net")) return(VA_BAD); /* Postmaster is always valid. */ if (!strcasecmp(lp,"postmaster")) return(VA_GOOD); ll = strlen(lp); if (stat(userlist,&stb) < 0) { /* No user list? */ return(VA_UNKNOWN); } if ( (nusers < 0) || (stb.st_mtime != file_stb.st_mtime) || (stb.st_dev != file_stb.st_dev) || (stb.st_ino != file_stb.st_ino) ) { if (reload_users()) return(VA_UNKNOWN); } if (nusers < 1) { /* *Still* no user list?! */ return(VA_UNKNOWN); } h = hash_block(lp,ll); if (tblsize) for (u=table[h%tblsize];u;u=u->link) if ((u->hash == h) && (u->nlen == ll) && !strncasecmp(lp,u->name,ll)) return(VA_GOOD); return(VA_BAD); }