diff -r 000000000000 -r 6f7a81934006 daemon/main.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/daemon/main.cc Wed Jan 16 22:39:43 2008 +0100 @@ -0,0 +1,194 @@ +// Copyright (C) 1999,2000 Bruce Guenter +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include +#include +#include +#include +#include +#include +#include +#include +#include "cli/cli.h" +#include "daemon.h" + +const char* cli_program = "vmailmgrd"; +const char* cli_help_prefix = "vmailmgr support daemon\n"; +const char* cli_help_suffix = ""; +const char* cli_args_usage = ""; +const int cli_args_min = 0; +const int cli_args_max = 0; +int opt_log_all = true; +int opt_verbose = false; + +// This program is the local server that controls the operation of many +// parts of this package. +// It is currently used to handle password checking and virtual user +// lookups, but will eventually contain the functionality to handle adding +// and deleting users and aliases, and changing passwords. +// +// F logs its activity to standard output, and as such +// requires its output to be piped through a tool to +// record those logs, such as F and F (from the +// F package), or +// F (included with the F package). + +cli_option cli_options[] = { + { 'd', 0, cli_option::flag, 0, &opt_log_all, + "Log only requests that fail", 0 }, + { 'D', 0, cli_option::flag, 1, &opt_log_all, + "Log all requests (default)", 0 }, + { 'v', 0, cli_option::flag, 0, &opt_verbose, + "Log non-verbosely (default)", 0 }, + { 'V', 0, cli_option::flag, 1, &opt_verbose, + "Log verbosely", 0 }, + // Log verbose messages regarding the system's status. + // Note that this flag implies C<-D>. + {0} +}; + +// SEE ALSO +// +// vdeliver(1), checkvpw(8) + +#define TIMEOUT 1 + +static inline void die(const char* msg) +{ + perror(msg); + exit(1); +} + +static void finishreq() +{ + alarm(0); + close(0); + close(1); +} + +static void abortreq(const char* m) +{ + logresponse(response(response::bad, m)); + finishreq(); +} + +static RETSIGTYPE handle_hup(int) +{ + signal(SIGHUP, handle_hup); + log("Stray SIGHUP caught"); +} + +static RETSIGTYPE handle_alrm(int) +{ + signal(SIGALRM, handle_alrm); + abortreq("Timed out waiting for remote"); + exit(1); +} + +static RETSIGTYPE handle_pipe(int) +{ + signal(SIGPIPE, handle_pipe); + abortreq("Connection to client lost"); + exit(1); +} + +static RETSIGTYPE handle_intr(int) +{ + signal(SIGINT, handle_intr); + signal(SIGTERM, handle_intr); + log("Stray interrupt caught"); +} + +bool decode_string(mystring& str, uchar*& buf, ssize_t& buflen) +{ + ssize_t length = (buf[0] << 8) | buf[1]; + buf += 2; buflen -= 2; + if(length > buflen) + return false; + str = mystring((char*)buf, length); + buf += length; buflen -= length; + return true; +} + +#define FAIL(MSG) do { abortreq(MSG ", aborting"); return 0; } while(0); + +command* decode_data(uchar* ptr, ssize_t length) +{ + uchar argcount = *ptr++; + --length; + mystring cmdstr; + if(!decode_string(cmdstr, ptr, length)) + FAIL("Couldn't decode the command string"); + if(cmdstr.empty()) + FAIL("Empty command string"); + command* cmd = new command(cmdstr, argcount); + for(unsigned i = 0; i < argcount; i++) { + mystring str; + if(!decode_string((*cmd)[i], ptr, length)) { + delete cmd; + FAIL("Error decoding a command parameter"); + } + } + return cmd; +} + +command* read_data() +{ + alarm(TIMEOUT); // avoid denial-of-service by faulty clients + + uchar hdrbuf[3]; + switch(read(0, &hdrbuf, 3)) { + case -1: FAIL("read system call failed or was interrupted"); + case 3: break; + default: FAIL("Short read while reading protocol header"); + } + if(hdrbuf[0] != 2) + FAIL("Invalid protocol from client"); + ssize_t length = (hdrbuf[1] << 8) | hdrbuf[2]; + uchar buf[length]; + if(read(0, buf, length) != length) + FAIL("Short read while reading message data"); + alarm(0); + return decode_data(buf, length); +} + +int cli_main(int, char**) +{ + if(opt_verbose) + opt_log_all = true; + + signal(SIGALRM, handle_alrm); + signal(SIGPIPE, handle_pipe); + signal(SIGINT, handle_intr); + signal(SIGTERM, handle_intr); + signal(SIGHUP, handle_hup); + signal(SIGQUIT, handle_intr); + + if(opt_verbose) + log("Accepted connection"); + command* cmd = read_data(); + if(cmd) { + response resp = dispatch_cmd(*cmd, 1); + logresponse(resp); + alarm(TIMEOUT); + if(!resp.write(1)) + abortreq("Error writing response"); + finishreq(); + delete cmd; + } + + return 0; +}