diff options
Diffstat (limited to 'ktalkd/ktalkd/machines/forwmach.cpp')
-rw-r--r-- | ktalkd/ktalkd/machines/forwmach.cpp | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/ktalkd/ktalkd/machines/forwmach.cpp b/ktalkd/ktalkd/machines/forwmach.cpp new file mode 100644 index 00000000..4d6b2146 --- /dev/null +++ b/ktalkd/ktalkd/machines/forwmach.cpp @@ -0,0 +1,442 @@ +/* + * Copyright (c) 1983 Regents of the University of California, (c) 1998 David Faure + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + * (BSD License, from kdelibs/doc/common/bsd-license.html) + */ + +#include "../includ.h" +#include "forwmach.h" +#include <stdio.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <string.h> +#include <syslog.h> +#include <errno.h> +#include <netdb.h> +#include <signal.h> +#include "../proto.h" +#include "../defs.h" +#include "../readconf.h" +#include "../process.h" +#include "../threads.h" + +ForwMachine * ForwMachine::pForwMachine = 0L; + +void sig_handler(int signum); + +ForwMachine::ForwMachine(const NEW_CTL_MSG * mp, + char * forward, + char * _forwardMethod, + int c_id_num) : pid(0), caller_id_num(c_id_num) +{ + // Store callee as 'local_user' + strncpy(local_user, mp->r_name, NEW_NAME_SIZE); + local_user[ NEW_NAME_SIZE - 1 ] = '\0'; + // -1 is to be sure to have a '\0' at the end + // Store caller's name + strncpy(caller_username, mp->l_name, NEW_NAME_SIZE); + caller_username[ NEW_NAME_SIZE - 1 ] = '\0'; + // Store caller's protocol + callerProtocol = (mp->vers==0) ? talkProtocol : ntalkProtocol; + + // Forward method : from string to enumerate + if (!strcmp(_forwardMethod,"FWA")) + forwardMethod = FWA; + else if (!strcmp(_forwardMethod,"FWR")) + forwardMethod = FWR; + else if (!strcmp(_forwardMethod,"FWT")) + forwardMethod = FWT; + else syslog(LOG_ERR,"Unknown forward method : %s",_forwardMethod); + + // Get answerer machine address and username + if (getNames(forward)) { + ktalk_debug("-- Talking to %s",answ_user); + ktalk_debug("-- On %s",answ_machine_name); + // Create a new talk connection, to the answerer ... + tcAnsw = new TalkConnection(answ_machine_addr, + answ_user, + // from caller's username + (char *) mp->l_name, + noProtocol); // to be checked + tcAnsw->open_sockets(); + // and from here if FWT or ... + if (forwardMethod != FWT) { + //from the caller if FWA or FWR + tcAnsw->set_addr(&mp->addr); + // but WE DO NOT CHANGE THE ctl_addr, we want the response !! + } + // Store caller's ctl_addr (to respond to its announce) + caller_ctl_addr = mp->ctl_addr; + // And his machine addr (to send a LOOK_UP) + caller_machine_addr = ((struct sockaddr_in * )(&mp->ctl_addr))->sin_addr; + } +} + +ForwMachine::~ForwMachine() +{ + delete answ_machine_name; + delete tcAnsw; + if (pid) kill(pid,SIGTERM); +} + +/** Fills private fields from forward + * @param forward user@host to forward the talk */ +int ForwMachine::getNames(char * forward) +{ /* taken from old get_names.c */ + register char *cp; + + /* strip out the machine name of the target */ + for (cp = forward; *cp && !strchr("@:!.", *cp); cp++) + ; + if (*cp == '\0') { + /* this is a forward to a local user */ + strncpy(answ_user, forward, NEW_NAME_SIZE); + answ_user[ NEW_NAME_SIZE -1 ] = '\0'; + answ_machine_name = new char[strlen(Options.hostname)+1]; + strcpy(answ_machine_name, Options.hostname); /* set by the daemon */ + } else { + if (*cp == '@') { + /* user@host */ + *cp++ = '\0'; + strncpy(answ_user, forward, NEW_NAME_SIZE); + answ_user[ NEW_NAME_SIZE -1 ] = '\0'; + answ_machine_name = new char[strlen(cp)+1]; + strcpy(answ_machine_name, cp); + } else { + /* host.user or host!user or host:user */ + *cp++ = '\0'; + strncpy(answ_user, cp, NEW_NAME_SIZE); + answ_user[ NEW_NAME_SIZE -1 ] = '\0'; + answ_machine_name = new char[strlen(forward)+1]; + strcpy(answ_machine_name, forward); + } + } + + struct hostent * hp = gethostbyname(answ_machine_name); + if (!hp) { + syslog(LOG_ERR, "gethostbyname for %s: %s", answ_machine_name, strerror(errno)); + return 0; + } + memcpy(&answ_machine_addr, hp->h_addr, hp->h_length); + return 1; +} + +int ForwMachine::isLookupForMe(const NEW_CTL_MSG * mp) +{ + /** We want to check if this LOOK_UP concerns this forwmachine. + * It does if : + mp->l_name = answ_user + mp->r_name = caller_username + mp->addr.sin_addr is 0.0.0.0, can't be tested ... + mp->ctl_addr.sin_addr could be tested but how ? + */ + if (Options.debug_mode) + { + syslog(LOG_DEBUG,"-- mp->l_name : '%s' answerer : '%s' mp->r_name : '%s' caller : '%s'", + mp->l_name, answ_user, mp->r_name, caller_username); + } + + return ( (!strcmp(mp->l_name, answ_user)) && + (!strcmp(mp->r_name, caller_username)) ); + +} + +char * ForwMachine::findMatch(NEW_CTL_MSG * mp) +{ + /** Check if there is a forwarding machine on this host, + * matching answerer = r_name and caller = l_name + * Then return the initial callee (local_user), to display in ktalkdlg + * This is used by local forwards, and therefore also if NEUBehaviour=1 */ + if (Options.debug_mode) + { + syslog(LOG_DEBUG,"-- mp->l_name : '%s' caller : '%s' mp->r_name : '%s' answerer : '%s'", + mp->l_name, caller_username, mp->r_name, answ_user); + } + if ((!strcmp(mp->l_name, caller_username)) && + (!strcmp(mp->r_name, answ_user)) ) + return local_user; + return NULL; +} + +int ForwMachine::transmit_chars(int sockt1, int sockt2, unsigned char * buf) +{ + int nb = read(sockt1, buf, BUFSIZ); + if (nb <= 0) { + return 0; // finished. + } + write(sockt2, buf, nb); + if ((nb <= 0) && (errno != EINTR)) { + syslog(LOG_ERR,"Unexpected error in write to socket"); + } + return nb; +} + +void ForwMachine::connect_FWT(TalkConnection * tcCaller) +{ + /** FWT : This is the method in which we take the connection to both + * clients and send each character received from one side to the other + * side. This allows to pass a firewall for instance. */ + /* debug("-- connect_FWT : Waiting for connection from Answerer (%s)", answ_user); */ + if (tcAnsw->accept()) + { + /* debug("-- connect_FWT : Trying to connect to Caller (%s)",caller_username); */ + if (tcCaller->connect()) + { + /* + debug("-- connect_FWT : Connected to caller (%s)", caller_username); + debug("-- connect_FWT : Connected to both. Let's go"); + */ + int socktC = tcCaller->get_sockt(); + int socktA = tcAnsw->get_sockt(); + int max_sockt = (socktC>socktA) ? socktC : socktA; + unsigned char buf[BUFSIZ]; + fd_set read_mask; + int nb; + int nbtot = 0; + for (;;) { + FD_ZERO(&read_mask); + FD_SET(socktA, &read_mask); // wait on both connections + FD_SET(socktC, &read_mask); + nb = select(max_sockt+1, &read_mask, NULL, NULL, NULL); // no timeout + if (nb <= 0) { + if (errno == EINTR) { + continue; + } + /* panic, we don't know what happened */ + TalkConnection::p_error("Unexpected error from select"); + } + if (FD_ISSET(socktA, &read_mask)) { + /* There is data on sockt A */ + nb = transmit_chars(socktA, socktC, buf); + if (nb==0) return ; + } + if (FD_ISSET(socktC, &read_mask)) { + /* There is data on sockt C */ + nb = transmit_chars(socktC, socktA, buf); + if (nb==0) return ; + nbtot += nb; + if (nbtot == 3) // just after the 3 edit chars + { + struct hostent * hp = gethostbyaddr((char *)&caller_machine_addr, + sizeof (struct in_addr), AF_INET); + if (hp != (struct hostent *)0) { + // Write first line for answerer. + // i18n() missing + sprintf((char *)buf, "Speaking to %s@%s\n", caller_username, hp->h_name); + write(socktA, (char *)buf, strlen((char *)buf)); + } else ktalk_debug("-- ERROR : Unable to resolve caller_machine_addr !"); + } + } + } // for + } else syslog(LOG_ERR,"-- FWT : Caller connected, but not answerer !"); + } else syslog(LOG_ERR,"-- FWT : Caller did not connect !"); +} + +void ForwMachine::sendResponse(const struct talk_addr target, NEW_CTL_RESPONSE * rp) +{ + if (rp->vers == 0) { // otalk protocol (internal coding for it) + rp->vers /*type in otalk*/ = rp->type; + rp->type /*answer in otalk*/ = rp->answer; + } + int cc = sendto(1 /*talkd_sockt*/, (char *) rp, + sizeof (NEW_CTL_RESPONSE), 0, (struct sockaddr *)&target, + sizeof (struct talk_addr)); + if (cc != sizeof (NEW_CTL_RESPONSE)) + syslog(LOG_WARNING, "sendto: %s", strerror(errno)); +} + +/** processAnnounce is done by a child (to let the daemon process other + * messages, including ours). Then the child is left running (he only knows the + * value of answ_id_num) and waits for SIGDELETE to use this value. */ +void ForwMachine::processAnnounce() +{ + if ((pid=fork())==0) // store pid in the parent + { + // Send announce to the answerer, and wait for response + ktalk_debug("-------------- ForwMachine : sending ANNOUNCE to %s",answ_user); + tcAnsw->ctl_transact(ANNOUNCE, caller_id_num); + // Copy answer and id_num from the response struct + ktalk_debug("-------------- ForwMachine : got a response"); + NEW_CTL_RESPONSE rp; // build our response struct + tcAnsw->getResponseItems(&rp.answer, &answ_id_num, 0L); + // answ_id_num stores id_num for delete. + rp.type = ANNOUNCE; + rp.vers = TALK_VERSION; + rp.id_num = htonl(our_id_num); + + ktalk_debug("Storing response id_num %d",answ_id_num); + // Now send the response to the caller + print_response("-- => response (processAnnounce)", &rp); + sendResponse(caller_ctl_addr, &rp); + // -- Now wait for SIGDELETE + + // store static ref to this forwmachine in this child. + pForwMachine = this; + // register signal hander + if (signal(SIGDELETE,&sig_handler)==SIG_ERR) ktalk_debug("ERROR for SIGUSR2"); + ktalk_debug("Signal handler registered. Waiting..."); + // infinite loop waiting for signals + while(1) + sleep(100); + } + ktalk_debug("Forwmachine started for Announce (now) and Delete (later). pid : %d",pid); + // new_process(); // We DON'T register new process. + // in case of re-announce, this forwmach will be forgotten. + // we don't want ktalkd to wait infinitely for it to die, it won't. +} + +/** Process the lookup in a child process. The current running child can't do + * it with a signal, but we need the answerer's ctl_addr to respond... */ +void ForwMachine::processLookup(const NEW_CTL_MSG * mp) +{ + if (fork()==0) + { // here we are the child + ktalk_debug("------------- Got LOOKUP : send it to caller (%s)", caller_username); + // Let's send a LOOK_UP on caller's machine, to make sure he still + // wants to speak to the callee... + TalkConnection * tcCaller = new TalkConnection(caller_machine_addr, + caller_username, + local_user, + callerProtocol); + tcCaller->open_sockets(); + tcCaller->look_for_invite(0/*no error if no invite*/); + NEW_CTL_RESPONSE rp; + tcCaller->getResponseItems(&rp.answer, &rp.id_num, &rp.addr); + ktalk_debug("------------- Done. Forward response to answerer"); + + rp.type = LOOK_UP; + rp.vers = mp->vers; + rp.id_num = htonl(rp.id_num); + // Now send the response to the answerer + if (forwardMethod == FWR) + { + // with caller's addr copied in the NEW_CTL_RESPONSE (if FWR), + // so that they can talk to each other. + /* rp.addr filled by getResponseItems */ + rp.addr.ta_family = htons(rp.addr.ta_family); + } + else // FWT. (FWA doesn't let us get the LOOK_UP) + { + // in this case, we copy in the NEW_CTL_RESPONSE the address + // of the connection socket set up here for the answerer + rp.addr = tcAnsw->get_addr(); + rp.addr.ta_family = htons(AF_INET); + } + print_response("-- => response (processLookup)", &rp); + if (forwardMethod == FWT) + tcAnsw->listen(); // start listening before we send the response, + // just in case the answerer is very fast (ex: answ mach) + sendResponse(mp->ctl_addr, &rp); + if (forwardMethod == FWT) + connect_FWT(tcCaller); + delete tcCaller; + _exit(0); + } + new_process(); +} + +/** Done by the forwmachine child that processed the ANNOUNCE. (He know answ_id_num) + * Exits at the end of the method */ +void ForwMachine::processDelete() +{ + // Send DELETE to the answerer, and don't wait for response + ktalk_debug("-------------- ForwMachine : sending DELETE to %s",answ_user); + ktalk_debug("Using resp->id_num %d",answ_id_num); + tcAnsw->ctl_transact(DELETE, answ_id_num); + _exit(0); // We exit the child, we have finished. +} + +// Static functions + +int ForwMachine::forwMachProcessLookup(TABLE_ENTRY * table, const NEW_CTL_MSG * mp) +{ + /** This goes through the table, looking for non-NULL fwm entries. + * After a cast to (ForwMachine *), the fwm entries allows us to + * speak to currently availabe ForwMachines, to handle correctly + * this LOOK_UP */ + ktalk_debug("-- forwMachProcessLookup(mp,rp)"); + TABLE_ENTRY *ptr; + for (ptr = table; ptr != 0L; ptr = ptr->next) { + if (ptr->fwm != 0L) + { + ForwMachine * fwm = (ForwMachine *) ptr->fwm; + if (fwm->isLookupForMe(mp)) { + ktalk_debug("-- Found match : id %d", ptr->request.id_num); + fwm->processLookup(mp); + return 1; + } + } + } + return 0; +} + +/** Check if there is a forwarding machine on this machine, + * matching answerer = r_name and caller = l_name + * Then set callee to the initial callee, to display in ktalkdlg */ +char * ForwMachine::forwMachFindMatch(TABLE_ENTRY * table, NEW_CTL_MSG * mp) +{ + ktalk_debug("-- forwMachFindMatch(mp)"); + TABLE_ENTRY *ptr; + char * callee; + for (ptr = table; ptr != 0L; ptr = ptr->next) { + if (ptr->fwm != 0L) + { + ForwMachine * fwm = (ForwMachine *) ptr->fwm; + callee = fwm->findMatch(mp); + if (callee) { + ktalk_debug("-- Found match : id %d", ptr->request.id_num); + return callee; + } + } + } + return NULL; +} + +void sig_handler(int signum) +{ + ktalk_debug("SIGNAL received : %d",signum); + ForwMachine * fwm = ForwMachine::getForwMachine(); + fwm->processDelete(); +} + +void ForwMachine::start(int o_id_num) +{ + our_id_num = o_id_num; + processAnnounce(); + +} + |