📄 inetfaxserver.c++
字号:
/* $Id: InetFaxServer.c++,v 1.10 2005/10/31 21:34:56 lhoward 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. */#include "port.h"#include "Dispatcher.h"#include "InetFaxServer.h"#include "Sys.h"#include "Socket.h"#include "config.h"#include <netdb.h>#include <ctype.h>extern "C" {#include <arpa/inet.h>#include <netinet/in_systm.h>#include <netinet/ip.h>}InetSuperServer::InetSuperServer(const char* p, int bl) : SuperServer("INET", bl) , port(p){ bindaddress = NULL;}InetSuperServer::~InetSuperServer() {}voidInetSuperServer::setBindAddress(const char *bindaddr){ bindaddress = bindaddr;}boolInetSuperServer::startServer(void){ 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, FAX_PROTONAME); if (!sp) { if (isdigit(cp[0])) sin.sin_port = htons(atoi(cp)); else sin.sin_port = htons(FAX_DEFPORT); } else sin.sin_port = sp->s_port; if (bindaddress) sin.sin_addr.s_addr = inet_addr(bindaddress); { int on = 1; if (Socket::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) >= 0) { if (Socket::bind(s, &sin, sizeof (sin)) >= 0) { (void) listen(s, getBacklog()); 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()); return (false);}HylaFAXServer* InetSuperServer::newChild(void) { return new InetFaxServer; }InetFaxServer* InetFaxServer::_instance = NULL;InetFaxServer::InetFaxServer(){ usedefault = true; swaitmax = 90; // wait at most 90 seconds swaitint = 5; // interval between retries memset(&data_dest, 0, sizeof (data_dest)); data_dest.sin_family = AF_INET; hostent* hp = Socket::gethostbyname(hostname); if (hp != NULL) { hostname = hp->h_name; struct in_addr in; memcpy(&in, hp->h_addr, sizeof (in)); hostaddr = inet_ntoa(in); } fxAssert(_instance == NULL, "Cannot create multiple InetFaxServer instances"); _instance = this;}InetFaxServer::~InetFaxServer() {}InetFaxServer& InetFaxServer::instance() { return *_instance; }voidInetFaxServer::initServer(void){ HylaFAXServer::initServer(); usedefault = true;}voidInetFaxServer::open(void){ setupNetwork(STDIN_FILENO); HylaFAXServer::open(); if (TRACE(CONNECT)) logInfo("HylaFAX INET connection from %s [%s]", (const char*) remotehost, (const char*) remoteaddr);}static const char*topDomain(const fxStr& h){ int dots = 0; u_int l = h.length(); if(l <= 0) return(NULL); // out early for (;;) { l = h.nextR(l, '.'); if (l == 0) return (&h[0]); // return whole string if (++dots == 2) return (&h[l]); l -=1; //back over the dot }}/* * Check whether host h is in our local domain, * defined as sharing the last two components of the domain part, * or the entire domain part if the local domain has only one component. * If either name is unqualified (contains no '.'), * assume that the host is local, as it will be * interpreted as such. */boolInetFaxServer::isLocalDomain(const fxStr& h){ const char* p1 = topDomain(hostname); const char* p2 = topDomain(h); return (p1 == NULL || p2 == NULL || strcasecmp(p1, p2) == 0);}/* * Check host identity returned by gethostbyaddr to * weed out clients trying to spoof us (this is mostly * a sanity check; if they have full control of DNS * they can still spoof) * Look up the name and check that the peer's address * corresponds to the host name. */boolInetFaxServer::checkHostIdentity(hostent*& hp){ fxStr name(hp->h_name); // must copy static value hp = Socket::gethostbyname(name); if (hp) { for (const char** cpp = (const char**) hp->h_addr_list; *cpp; cpp++) if (memcmp(*cpp, &peer_addr.sin_addr, hp->h_length) == 0) return (true); reply(130, "Warning, client address \"%s\" is not listed for host name \"%s\".", (const char*) remoteaddr, hp->h_name); } else reply(130, "Warning, no inverse address mapping for client host name \"%s\".", (const char*) name); return (false);}voidInetFaxServer::setupNetwork(int fd){ socklen_t addrlen; addrlen = sizeof (peer_addr); if (Socket::getpeername(fd, &peer_addr, &addrlen) < 0) { logError("getpeername: %m (incorrect hfaxd invocation?)"); dologout(-1); } addrlen = sizeof (ctrl_addr); if (Socket::getsockname(fd, &ctrl_addr, &addrlen) < 0) { logError("getsockname: %m"); dologout(-1); }#if defined(IPTOS_LOWDELAY) { int tos = IPTOS_LOWDELAY; if (Socket::setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)) < 0) logWarning("setsockopt (IP_TOS): %m"); }#endif#if defined(SO_LINGER) && !defined(__linux__) { struct linger opt; opt.l_onoff = 1; opt.l_linger = 60; if (Socket::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof (opt)) < 0) logWarning("setsockopt (SO_LINGER): %m"); }#endif /* anchor socket to avoid multi-homing problems */ data_source = ctrl_addr; data_source.sin_port = htons(ntohs(ctrl_addr.sin_port)-1);#ifdef F_SETOWN if (fcntl(fd, F_SETOWN, getpid()) == -1) logError("fcntl (F_SETOWN): %m");#endif#if defined(SO_OOBINLINE) /* handle urgent data inline */ { int on = 1; if (Socket::setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &on, sizeof (on)) < 0) logError("setsockopt (SO_OOBINLINE): %m"); }#endif#ifdef SIGURG /* * XXX This is #ifdef'd for ISC but indicates a problem * XXX in the underlying system. Without support for * XXX urgent data clients will not be able to abort an * XXX ongoing operation such as a file transfer. */ signal(SIGURG, fxSIGHANDLER(sigURG));#endif signal(SIGPIPE, fxSIGHANDLER(sigPIPE)); hostent* hp = gethostbyaddr((char*) &peer_addr.sin_addr, sizeof (struct in_addr), AF_INET); remoteaddr = inet_ntoa(peer_addr.sin_addr); if (remoteaddr == "0.0.0.0") remotehost = "localhost"; else if (hp && checkHostIdentity(hp)) remotehost = hp->h_name; else remotehost = remoteaddr; Dispatcher::instance().link(STDIN_FILENO, Dispatcher::ReadMask, this);}voidInetFaxServer::handleUrgentData(void){ /* * Only process the urgent data is we are expecting it. * Specifically, if a file transfer, synchronous job * wait, or other potentially long-running operation is * taking place we do a setjmp and set a flag so that * if the client requests an abort operation (ABOR) then * we longjmp below to break out of the op. */ if ((state & (S_TRANSFER|S_WAITTRIG)) == 0) { if (IS(WAITFIFO)) { fifoResponse = "User aborted operation"; state &= ~S_WAITFIFO; } return; } char line[8]; if (!getCmdLine(line, sizeof (line))) { reply(221, "You could at least say goodbye."); dologout(0); } else if (strcasecmp(line, "ABOR\n") == 0) { /* * Abort the operation in progress. Two reply * codes are sent on the control channel; one * for the outstanding operation and one for the * abort command. */ reply(426, "Operation aborted. Data connection closed."); reply(226, "Abort successful"); longjmp(urgcatch, 1); // XXX potential memory leak } else if (strcasecmp(line, "STAT\n") == 0) { if (file_size != (off_t) -1) reply(213, "Status: %lu of %lu bytes transferred", byte_count, file_size); else reply(213, "Status: %lu bytes transferred", byte_count); } else pushCmdData(line, strlen(line));}void InetFaxServer::sigURG(int) { InetFaxServer::instance().handleUrgentData();}voidInetFaxServer::lostConnection(void){ if (TRACE(PROTOCOL)) logDebug("Lost connection to %s [%s]", (const char*) remotehost, (const char*) remoteaddr); dologout(-1);}void InetFaxServer::sigPIPE(int) { InetFaxServer::instance().lostConnection(); }static boolsetupPassiveDataSocket(int pdata, struct sockaddr_in& pasv_addr){ socklen_t len = sizeof (pasv_addr); return ( Socket::bind(pdata, &pasv_addr, sizeof (pasv_addr)) >= 0 && Socket::getsockname(pdata, &pasv_addr, &len) >= 0 && listen(pdata, 1) >= 0 );}#define UC(b) (((int) b) & 0xff)/* * Note: a response of 425 is not mentioned as a possible response to the * PASV command in RFC959. However, it has been blessed as a legitimate * response by Jon Postel in a telephone conversation with Rick Adams on 25 * Jan 89. */voidInetFaxServer::passiveCmd(void){ if (pdata < 0) { pdata = socket(AF_INET, SOCK_STREAM, 0); if (pdata >= 0) { pasv_addr = ctrl_addr; pasv_addr.sin_port = 0; if (!setupPassiveDataSocket(pdata, pasv_addr)) (void) Sys::close(pdata), pdata = -1; } } if (pdata >= 0) { const u_char* a = (const u_char*) &pasv_addr.sin_addr; const u_char* p = (const u_char*) &pasv_addr.sin_port; reply(227, "Entering passive mode (%d,%d,%d,%d,%d,%d)", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1])); } else perror_reply(425, "Cannot setup passive connection", errno);}voidInetFaxServer::printaddr(FILE* fd, const char* leader, const struct sockaddr_in& sin){ const u_char* a = (const u_char *) &sin.sin_addr; const u_char* p = (const u_char *) &sin.sin_port; fprintf(fd, "%s (%d,%d,%d,%d,%d,%d)\r\n", leader, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));}voidInetFaxServer::netStatus(FILE* fd){ if (data != -1) fprintf(fd, " Client data connection open\r\n"); else if (pdata != -1) printaddr(fd, " In passive mode", pasv_addr); else if (!usedefault) printaddr(fd, " PORT", data_dest); else fprintf(fd, " No client data connection\r\n");}/* * Creat a socket for a data transfer. */FILE*InetFaxServer::getDataSocket(const char* mode){ if (data >= 0) return (fdopen(data, mode)); int s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) goto bad; { int on = 1; if (Socket::setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) goto bad; } { int ntry; // XXX for __GNUC__ for (ntry = 1;; ntry++) { if (Socket::bind(s, &data_source, sizeof (data_source)) >= 0) break; if (errno != EADDRINUSE || ntry > 10) goto bad; sleep(ntry); } }#ifdef IPTOS_THROUGHPUT { int on = IPTOS_THROUGHPUT; if (Socket::setsockopt(s, IPPROTO_IP, IP_TOS, &on, sizeof (on)) < 0) logWarning("setsockopt (IP_TOS): %m"); }#endif return (fdopen(s, mode));bad: (void) Sys::close(s); return (NULL);}boolInetFaxServer::dataConnect(void){ return Socket::connect(data, &data_dest,sizeof (data_dest)) >= 0;}/* * Establish a data connection for a file transfer operation. */FILE*InetFaxServer::openDataConn(const char* mode, int& code){ byte_count = 0; if (pdata >= 0) { // passive mode, wait for connection struct sockaddr_in from; socklen_t fromlen = sizeof (from); // XXX don't block, select....???? int s = Socket::accept(pdata, (struct sockaddr*) &from, &fromlen); if (s < 0) { reply(425, "Cannot open data connection."); (void) Sys::close(pdata); pdata = -1; return (NULL); } // XXX verify peer address??? (void) Sys::close(pdata); pdata = s;#ifdef IPTOS_LOWDELAY int tos = IPTOS_LOWDELAY; (void) Socket::setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof (tos));#endif code = 150; return (fdopen(pdata, mode)); } if (data >= 0) { code = 125; usedefault = 1; return (fdopen(data, mode)); } if (usedefault) data_dest = peer_addr; usedefault = 1; FILE* file = getDataSocket(mode); if (file == NULL) { reply(425, "Cannot create data socket (%s,%d): %s.", inet_ntoa(data_source.sin_addr), ntohs(data_source.sin_port), strerror(errno)); return (NULL); } data = fileno(file); for (int swait = 0; !dataConnect(); swait += swaitint) { if (!(errno == EADDRINUSE || errno == EINTR) || swait >= swaitmax) { perror_reply(425, "Cannot build data connection", errno); fclose(file); data = -1; return (NULL); } sleep(swaitint); } code = 150; return (file);}boolInetFaxServer::hostPort(){ long a0, a1, a2, a3; long p0, p1; bool syntaxOK = NUMBER(a0) && COMMA() && NUMBER(a1) && COMMA() && NUMBER(a2) && COMMA() && NUMBER(a3) && COMMA() && NUMBER(p0) && COMMA() && NUMBER(p1) ; if (syntaxOK) { u_char* a = (u_char*) &data_dest.sin_addr; u_char* p = (u_char*) &data_dest.sin_port; a[0] = UC(a0); a[1] = UC(a1); a[2] = UC(a2); a[3] = UC(a3); p[0] = UC(p0); p[1] = UC(p1); return (true); } else return (false);}voidInetFaxServer::portCmd(void){ logcmd(T_PORT, "%s;%u", inet_ntoa(data_dest.sin_addr), data_dest.sin_port); usedefault = false; if (pdata >= 0) (void) Sys::close(pdata), pdata = -1; ack(200, cmdToken(T_PORT));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -