?? snppserver.c++
字號:
/* $Id: SNPPServer.c++,v 1.14 2006/03/27 21:36:28 aidan Exp $ *//* * Copyright (c) 1995-1996 Sam Leffler * Copyright (c) 1995-1996 Silicon Graphics, Inc. * HylaFAX is a trademark of Silicon Graphics * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that (i) the above copyright notices and this permission notice appear in * all copies of the software and related documentation, and (ii) the names of * Sam Leffler and Silicon Graphics may not be used in any advertising or * publicity relating to the software without the specific, prior written * permission of Sam Leffler and Silicon Graphics. * * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */#ifdef SNPP_SUPPORT/* * Simple Network Paging Protocol (SNPP) Support. */#include "config.h"#include "Sys.h"#include "Socket.h"#include "SNPPServer.h"#include "Dispatcher.h"#include "RE.h"#include <ctype.h>#if HAS_CRYPT_H#include <crypt.h>#endifextern "C" {#include <netdb.h>#include <arpa/inet.h>#include <netinet/in_systm.h>#include <netinet/ip.h>}SNPPSuperServer::SNPPSuperServer(const char* p, int bl) : SuperServer("SNPP", bl) , port(p){}SNPPSuperServer::~SNPPSuperServer() {}boolSNPPSuperServer::startServer(void){ /* * Switch to super-user to do the bind in case * we are to use a port in the privileged region * on a BSD system (ports <=1024 are reserved * and the default SNPP port is 444). * * NB: We do it for both the socket+bind calls * to workaround a bug in Solaris 2.5. */ uid_t ouid = geteuid(); (void) seteuid(0); int s = socket(AF_INET, SOCK_STREAM, 0); if (s >= 0) { struct sockaddr_in sin; memset(&sin, 0, sizeof (sin)); sin.sin_family = AF_INET; const char* cp = port; struct servent* sp = getservbyname(cp, SNPP_PROTONAME); if (!sp) { if (isdigit(cp[0])) sin.sin_port = htons(atoi(cp)); else sin.sin_port = htons(SNPP_DEFPORT); } else sin.sin_port = sp->s_port; if (Socket::bind(s, &sin, sizeof (sin)) >= 0) { (void) listen(s, getBacklog()); (void) seteuid(ouid); Dispatcher::instance().link(s, Dispatcher::ReadMask, this); return (true); // success } Sys::close(s); logError("HylaFAX %s: bind (port %u): %m", getKind(), ntohs(sin.sin_port)); } else logError("HylaFAX %s: socket: %m", getKind()); (void) seteuid(ouid); return (false);}HylaFAXServer* SNPPSuperServer::newChild(void) { return new SNPPServer; }SNPPServer::SNPPServer() {}SNPPServer::~SNPPServer() {}voidSNPPServer::open(void){ setupNetwork(STDIN_FILENO); initServer(); // complete state initialization fxStr emsg; if (!initClientFIFO(emsg)) { logInfo("connection refused (%s) from %s [%s]", (const char*) emsg, (const char*) remotehost, (const char*) remoteaddr); reply(420, "%s server cannot initialize: %s", (const char*) hostname, (const char*) emsg); dologout(-1); } ctrlFlags = fcntl(STDIN_FILENO, F_GETFL); // for parser if (isShutdown(true)) { reply(421, "%s SNPP server unavailable; try again later.", (const char*) hostname); dologout(-1); } reply(220, "%s SNPP server (%s) ready.", (const char*) hostname, version); if (TRACE(CONNECT)) logInfo("SNPP connection from %s [%s]", (const char*) remotehost, (const char*) remoteaddr);}voidSNPPServer::initServer(void){ InetFaxServer::initServer(); state &= ~S_USEGMT; // SNPP uses local times resetState();}voidSNPPServer::initDefaultJob(void){ InetFaxServer::initDefaultJob(); defJob.jobtype = "pager"; initSNPPJob();}voidSNPPServer::initSNPPJob(void){ defJob.queued = false; // jobs not queued--per protocol defJob.maxdials = SNPP_DEFREDIALS; // configuration defaults... defJob.maxtries = SNPP_DEFRETRIES; defJob.killtime = 60*killMap[SNPP_DEFLEVEL]; defJob.retrytime = retryMap[SNPP_DEFLEVEL]; defJob.usrpri = priMap[SNPP_DEFLEVEL]; // XXX default notification}voidSNPPServer::resetState(void){ initDefaultJob(); // default job state msgFile = ""; haveText = false; // no message text yet msgs.resize(0); // purge any message refs}voidSNPPServer::dologout(int status){ /* * Purge any partially constructed jobs. If we were * doing a SEND when the connection was dropped then * we do not purge jobs but instead just let them run * detached. This may not be the best thing; we might * instead want to kill the jobs--we'll need to think * about this some more before changing this behaviour. */ if (msgs.length() > 0 && !IS(SENDWAIT)) { fxStr emsg; for (u_int i = 0, n = msgs.length(); i < n; i++) { Job* job = findJob(msgs[i], emsg); if (job) { for (u_int j = 0, m = job->items.length(); j < m; j++) if (job->items[j].op == FaxRequest::send_data) (void) Sys::unlink(job->items[j].item); (void) Sys::unlink(job->qfile); delete job; } } } InetFaxServer::dologout(status);}static inline boolisMagic(char c){ return (c == '[' || c == ']' || c == '*' || c == '.' || c == '^' || c == '$' || c == '-' || c == '+' || c == '{' || c == '}' || c == '(' || c == ')');}/* * Handle \escapes for a pager ID replacement string. */static voidsubRHS(fxStr& result, const RE& re, const fxStr& match){ /* * Do ``&'' and ``\n'' interpolations in the replacement. */ for (u_int i = 0, n = result.length(); i < n; i++) { if (result[i] == '\\') { // process \<char> escapes result.remove(i), n--; if (isdigit(result[i])) { int mn = result[i] - '0'; int ms = re.StartOfMatch(mn); int mlen = re.EndOfMatch(mn) - ms; result.remove(i); // delete \n if (ms >= 0) result.insert(match.extract(ms, mlen), i); else logError("Invalid backreference in pagermap: \\%d", mn); n = result.length(); // adjust string length ... i += mlen - 1; // ... and scan index } } else if (result[i] == '&') { // process & replacement int ms = re.StartOfMatch(0); int mlen = re.EndOfMatch(0) - ms; result.remove(i); // delete & result.insert(match.extract(ms, mlen), i); n = result.length(); // adjust string length ... i += mlen - 1; // ... and scan index } }}/* * Given a client-supplied pager ID return the phone number * to dial of the service provider and the PIN to supply * to the provider when talking IXO/TAP (for aliases). */boolSNPPServer::mapPagerID(const char* pagerID, fxStr& number, fxStr& pin, fxStr& emsg){ if (pagerIDMapFile != "") { FILE* fd = fopen(fixPathname(pagerIDMapFile), "r"); if (fd != NULL) { char buf[1024]; while (fgets(buf, sizeof (buf), fd)) { char* cp; for (cp = buf; isspace(*cp); cp++) // leading whitespace ; if (*cp == '#' || *cp == '\0') continue; /* * Syntax is: * * <pattern> <dialstring>[/<PIN>] * * where <pattern> can be a simple string of alpha * numerics or a regular expression. The first line * that matches the client-specified pager ID is used. * If no <PIN> is specified then the client-specified * string is sent to the provider. If <dialstring> * is "reject" (verbatim) then matches are rejected. * <PIN> is treated as the RHS of an RE-style substitution: * \n and & escapes are replaced according to the RE * matching work. * * Leading white space on a line is ignored. Lines * that begin with '#' are ignored (i.e. comments). */ const char* pattern = cp; bool isRE = false; for (; *cp != '\0' && !isspace(*cp); cp++) if (isMagic(*cp)) isRE = true; if (*cp != '\0') // \0-term. <pattern> *cp++ = '\0'; bool match; RE* re; if (isRE) { re = new RE(pattern); match = re->Find(pagerID, strlen(pagerID)); } else { match = streq(pattern, pagerID); re = NULL; } if (match) { while (isspace(*cp)) // leading whitespace cp++; if (*cp != '\n' && *cp != '\0') { // got <dialstring> char* np = strchr(cp, '\n'); // remove trailing \n if (np) *np = '\0'; np = cp; for (; *cp && *cp != '/'; cp++) ; if (*cp == '/') { // <dialstring>/<PIN> *cp++ = '\0'; number = np; pin = cp; if (re) { // do substitutions subRHS(pin, *re, pagerID); subRHS(number, *re, pagerID); } } else { // <dialstring> number = np; pin = pagerID; } delete re; (void) fclose(fd); fxStr s(number); s.raisecase(); if (s == "REJECT") { emsg = fxStr::format("Invalid pager ID %s",pagerID); return (false); } else return (true); } } delete re; } (void) fclose(fd); emsg = fxStr::format("Unknown or illegal pager ID %s", pagerID); } else { emsg = fxStr::format("Cannot open pager ID mapping file %s (%s)", (const char*) pagerIDMapFile, strerror(errno)); logError("%s", (const char*) emsg); } } else { emsg = "No pager ID mapping file found"; logError("%s", (const char*) emsg); } return (false);}/* * SNPP Parser. */#define N(a) (sizeof (a) / sizeof (a[0]))/* * Standard protocol commands. */static const tab cmdtab[] = {{ "2WAY", T_2WAY, true,false, "(preface 2-way transaction)" },{ "ABOR", T_ABOR, false, true, "(abort operation)" },{ "ACKR", T_ACKREAD, true,false, "0|1" },{ "ALER", T_ALERT, true,false, "alert-level" },{ "CALL", T_CALLERID, true,false, "caller-ID" },{ "COVE", T_COVERAGE, true,false, "alternate-area" },{ "DATA", T_DATA, true, true, "(specify multi-line message)" },{ "EXPT", T_EXPTAG, true,false, "hours" },{ "HELP", T_HELP, false, true, "[<string>]" },{ "HOLD", T_HOLDUNTIL, true, true, "YYYYMMDDHHMMSS [+/-GMT-diff]" },{ "KTAG", T_KTAG, true,false, "message-tag pass-code" },{ "LEVE", T_LEVEL, true, true, "service-level" },{ "LOGI", T_LOGIN, false, true, "username [password]" },{ "MCRE", T_MCRESPONSE, true,false, "2-byte-code response-text" },{ "MESS", T_MESSAGE, true, true, "message" },{ "MSTA", T_MSTATUS, true,false, "message-tag pass-code" },{ "NOQU", T_NOQUEUEING, true,false, "(disable message queueing)" },{ "PAGE", T_PAGER, true, true, "pager-ID|alias [PIN]" },{ "PING", T_PING, true, true, "pager-ID|alias" },{ "QUIT", T_QUIT, false, true, "(terminate service)" },{ "RESE", T_REST, true, true, "(reset state)" },{ "RTYP", T_RTYPE, true,false, "reply-type-code" },{ "SEND", T_SEND, true, true, "(send message)" },{ "SITE", T_SITE, true, true, "site-cmd [arguments]" },{ "STAT", T_STAT, false, true, "(return server status)" },{ "SUBJ", T_SUBJECT, true, true, "message-subject" },};/* * Site-specific commands. */static const tab sitetab[] = {{ "FROMUSER", T_FROM_USER, true, true, "[<string>]" },{ "HELP", T_HELP, false, true, "[<string>]" },{ "IDLE", T_IDLE, true, true, "[max-idle-timeout]" },{ "JPARM", T_JPARM, true, true, "(print job parameter status)" },{ "JQUEUE", T_JQUEUE, true, true, "[on|off]" },{ "LASTTIME", T_LASTTIME, false, true, "[DDHHSS]" },{ "MAXDIALS", T_MAXDIALS, true, true, "[<number>]" },{ "MAXTRIES", T_MAXTRIES, true, true, "[<number>]" },{ "MODEM", T_MODEM, true, true, "[device|class]" },{ "NOTIFY", T_NOTIFY, true, true, "[NONE|DONE|REQUEUE|DONE+REQUEUE]"},{ "MAILADDR", T_NOTIFYADDR, true, true, "[email-address]" },{ "RETRYTIME", T_RETRYTIME, true, true, "[HHSS]" },{ "SCHEDPRI", T_SCHEDPRI, true, true, "[<number>]" },};static const tab*lookup(const tab* p, u_int n, const char* cmd){ while (n != 0) { if (strneq(cmd, p->name, 4)) // NB: match on first 4 chars return (p); p++, n--; } return (NULL);}static const char*tokenName(const tab* p, u_int n, Token t){ while (n != 0) { if (p->token == t) return (p->name); p++, n--; } return ("???");}const char*SNPPServer::cmdToken(Token t){ return tokenName(cmdtab, N(cmdtab), t);}const char*SNPPServer::siteToken(Token t){ return tokenName(sitetab, N(sitetab), t);}boolSNPPServer::checklogin(Token t){ if (!IS(LOGGEDIN)) { cmdFailure(t, "not logged in"); reply(530, "Please login with LOGIN."); return (false); } else return (true);}/* * Parse and process command input received on the * control channel. This method is invoked whenever * data is present on the control channel. We read * everything and parse (and execute) as much as possible * but do not block waiting for more data except when * a partial line of input is received. This is done * to ensure other processing will be handled in a * timely fashion (e.g. processing of messages from * the scheduler received via the FIFO). */intSNPPServer::parse(){ if (IS(WAITDATA)) { // recursive invocation state &= ~S_WAITDATA; return (0); } if (IS(SENDWAIT)) { // waiting in SEND, discard data while (getCmdLine(cbuf, sizeof (cbuf))) ; return (0); } // stop control channel idle timeout Dispatcher::instance().stopTimer(this); pushToken(T_NIL); // reset state for (;;) { /* * Fetch the next complete line of input received on the * control channel. This call will fail when no more data * is *currently* waiting on the control channel. Note * that this does not mean the connection has dropped; just * that data is not available at this instant. Note also * that if a partial line of input is received a complete * line will be waited for (see below). */ if (!getCmdLine(cbuf, sizeof (cbuf))) break; /* * Parse the line of input read above. */ if (getToken(T_STRING, "command token")) { tokenBody.raisecase(); const tab* p = lookup(cmdtab, N(cmdtab), tokenBody); if (p == NULL) reply(500, "%s: Command not recognized.", (const char*) tokenBody); else if (!p->implemented)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -