diff --git a/Makefile b/Makefile index 2a2ecec..37701d0 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CFLAGS=-g -O1 -Wall -Wno-switch -Wextra -fstack-protector-strong -D_FORTIFY_SOURCE=2 -ALL = maddr mdeliver mdirs mflag mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread +ALL = maddr mdeliver mdirs mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread all: $(ALL) @@ -8,6 +8,7 @@ maddr: maddr.o blaze822.o seq.o rfc2047.o mymemmem.o mdeliver: mdeliver.o blaze822.o mymemmem.o mdirs: mdirs.o mflag: mflag.o blaze822.o seq.o mymemmem.o +mgenmid: mgenmid.o blaze822.o seq.o mymemmem.o mhdr: mhdr.o blaze822.o seq.o rfc2047.o mymemmem.o minc: minc.o mlist: mlist.o diff --git a/man/mgenmid.1 b/man/mgenmid.1 new file mode 100644 index 0000000..8cf2d96 --- /dev/null +++ b/man/mgenmid.1 @@ -0,0 +1,58 @@ +.Dd August 1, 2016 +.Dt MGENMID 1 +.Os +.Sh NAME +.Nm mgenmid +.Nd generate a Message-ID +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +generates a fresh Message-ID and prints it. +The Message-ID consists of a timestamp, +a random value, +and a fully qualified domain name. +.Pp +The fully qualified domain name is computed by: +.Bl -enum +.It +Using +.Sq Li "FQDN:" +from +.Pa "~/.santoku/profile" +(if set). +.It +Resolving the current hostname. +.It +Using the host part of the address in +.Sq Li "Local-Mailbox:" +from +.Pa "~/.santoku/profile" +(if set). +.El +.Pp +If these steps don't result in a fully qualified domain name, +.Nm +fails. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Rs +.%A M. Curtin +.%A J. Zawinski +.%D July 1998 +.%R draft-ietf-usefor-message-id-01.txt +.%T Recommendations for generating Message IDs +.Re +.Sh AUTHORS +.An Christian Neukirchen Aq Mt chneukirchen@gmail.com +.Sh LICENSE +.Nm +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/mgenmid.c b/mgenmid.c new file mode 100644 index 0000000..6122519 --- /dev/null +++ b/mgenmid.c @@ -0,0 +1,115 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "blaze822.h" + +void +printb36(uint64_t x) +{ + static char const base36[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + char outbuf[16]; + char *o = outbuf + sizeof outbuf; + *o = 0; + + do { *--o = base36[x % 36]; } while (x /= 36); + fputs(o, stdout); +} + +int main() +{ + char hostbuf[1024]; + char *host = 0; + + char *f = blaze822_home_file(".santoku/profile"); + struct message *config = blaze822(f); + + if (config) // try FQDN: first + host = blaze822_hdr(config, "fqdn"); + + if (!host && gethostname(hostbuf, sizeof hostbuf) == 0) { + // termination not posix guaranteed + hostbuf[sizeof hostbuf - 1] = 0; + + struct addrinfo hints = { .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_CANONNAME }; + struct addrinfo *info; + if (getaddrinfo(hostbuf, 0, &hints, &info) == 0) { + // sanity checks: no (null), at least one dot, + // doesn't start with localhost. + + if (info && + info->ai_canonname && + strchr(info->ai_canonname, '.') && + strncmp(info->ai_canonname, "localhost.", 10) != 0) + host = info->ai_canonname; + } + } + + if (!host && config) { + // get address part of Local-Mailbox: + char *disp, *addr; + char *from = blaze822_hdr(config, "local-mailbox"); + while (from && (from = blaze822_addr(from, &disp, &addr))) + if (addr) { + host = strchr(addr, '@'); + if (host) { + host++; + break; + } + } + } + + if (!host) { + fprintf(stderr, + "mgenmid: failed to find a FQDN for the Message-ID.\n" + " Define 'FQDN:' or 'Local-Mailbox:' in ~/.santoku/profile\n" + " or add a FQDN to /etc/hosts.\n"); + exit(1); + } + + struct timespec tp; + clock_gettime(CLOCK_REALTIME, &tp); + + uint64_t rnd; + + int rndfd = open("/dev/urandom", O_RDONLY); + if (rndfd >= 0) { + unsigned char rndb[8]; + if (read(rndfd, rndb, sizeof rndb) != sizeof rndb) + goto fallback; + close(rndfd); + + int i; + for (i = 0, rnd = 0; i < 8; i++) + rnd = rnd*256 + rndb[i]; + } else { +fallback: + srand48(tp.tv_sec ^ tp.tv_nsec/1000 ^ getpid()); + rnd = (lrand48() << 32) + lrand48(); + } + + rnd |= (1LL << 63); // set highest bit to force full width + + putchar('<'); + printb36(((uint64_t)tp.tv_sec << 16) + (tp.tv_nsec >> 16)); + putchar('.'); + printb36(rnd); + putchar('@'); + fputs(host, stdout); + putchar('>'); + putchar('\n'); + + return 0; +}