Back to index

citadel  8.12
serv_networkclient.c
Go to the documentation of this file.
00001 /*
00002  * This module handles shared rooms, inter-Citadel mail, and outbound
00003  * mailing list processing.
00004  *
00005  * Copyright (c) 2000-2012 by the citadel.org team
00006  *
00007  *  This program is open source software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 3 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program; if not, write to the Free Software
00019  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  *
00021  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
00022  * This is a fairly high-level type of critical section.  It ensures that no
00023  * two threads work on the netconfigs files at the same time.  Since we do
00024  * so many things inside these, here are the rules:
00025  *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
00026  *  2. Do *not* perform any I/O with the client during these sections.
00027  *
00028  */
00029 
00030 
00031 #include "sysdep.h"
00032 #include <stdlib.h>
00033 #include <unistd.h>
00034 #include <stdio.h>
00035 #include <fcntl.h>
00036 #include <ctype.h>
00037 #include <signal.h>
00038 #include <pwd.h>
00039 #include <errno.h>
00040 #include <sys/stat.h>
00041 #include <sys/types.h>
00042 #include <dirent.h>
00043 #if TIME_WITH_SYS_TIME
00044 # include <sys/time.h>
00045 # include <time.h>
00046 #else
00047 # if HAVE_SYS_TIME_H
00048 #  include <sys/time.h>
00049 # else
00050 #  include <time.h>
00051 # endif
00052 #endif
00053 #ifdef HAVE_SYSCALL_H
00054 # include <syscall.h>
00055 #else 
00056 # if HAVE_SYS_SYSCALL_H
00057 #  include <sys/syscall.h>
00058 # endif
00059 #endif
00060 
00061 #include <sys/wait.h>
00062 #include <string.h>
00063 #include <limits.h>
00064 #include <libcitadel.h>
00065 #include "citadel.h"
00066 #include "server.h"
00067 #include "citserver.h"
00068 #include "support.h"
00069 #include "config.h"
00070 #include "user_ops.h"
00071 #include "database.h"
00072 #include "msgbase.h"
00073 #include "internet_addressing.h"
00074 #include "serv_network.h"
00075 #include "clientsocket.h"
00076 #include "file_ops.h"
00077 #include "citadel_dirs.h"
00078 #include "threads.h"
00079 
00080 #ifndef HAVE_SNPRINTF
00081 #include "snprintf.h"
00082 #endif
00083 
00084 #include "context.h"
00085 
00086 #include "netconfig.h"
00087 #include "ctdl_module.h"
00088 
00089 struct CitContext networker_client_CC;
00090 
00091 #define NODE ChrPtr(((AsyncNetworker*)IO->Data)->node)
00092 #define N ((AsyncNetworker*)IO->Data)->n
00093 
00094 int NetworkClientDebugEnabled = 0;
00095 
00096 #define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (NetworkClientDebugEnabled != 0))
00097 
00098 #define EVN_syslog(LEVEL, FORMAT, ...) \
00099        DBGLOG(LEVEL) syslog(LEVEL, \
00100               "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT, \
00101               IO->ID, CCID, NODE, N, __VA_ARGS__)
00102 
00103 #define EVNM_syslog(LEVEL, FORMAT) \
00104        DBGLOG(LEVEL) syslog(LEVEL, \
00105               "IO[%ld]CC[%d]NW[%s][%ld]" FORMAT, \
00106               IO->ID, CCID, NODE, N)
00107 
00108 #define EVNCS_syslog(LEVEL, FORMAT, ...) \
00109        DBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT, \
00110               IO->ID, NODE, N, __VA_ARGS__)
00111 
00112 #define EVNCSM_syslog(LEVEL, FORMAT) \
00113        DBGLOG(LEVEL) syslog(LEVEL, "IO[%ld]NW[%s][%ld]" FORMAT, \
00114               IO->ID, NODE, N)
00115 
00116 
00117 typedef enum _eNWCState {
00118        eeGreating,
00119        eAuth,
00120        eNDOP,
00121        eREAD,
00122        eReadBLOB,
00123        eCLOS,
00124        eNUOP,
00125        eWRIT,
00126        eWriteBLOB,
00127        eUCLS,
00128        eQUIT
00129 }eNWCState;
00130 
00131 
00132 typedef struct _async_networker {
00133         AsyncIO IO;
00134        DNSQueryParts HostLookup;
00135        eNWCState State;
00136        long n;
00137         StrBuf *SpoolFileName;
00138         StrBuf *tempFileName;
00139        StrBuf *node;
00140        StrBuf *host;
00141        StrBuf *port;
00142        StrBuf *secret;
00143        StrBuf        *Url;
00144 } AsyncNetworker;
00145 
00146 typedef eNextState(*NWClientHandler)(AsyncNetworker* NW);
00147 eNextState nwc_get_one_host_ip(AsyncIO *IO);
00148 
00149 eNextState nwc_connect_ip(AsyncIO *IO);
00150 
00151 eNextState NWC_SendQUIT(AsyncNetworker *NW);
00152 eNextState NWC_DispatchWriteDone(AsyncIO *IO);
00153 
00154 void DeleteNetworker(void *vptr)
00155 {
00156        AsyncNetworker *NW = (AsyncNetworker *)vptr;
00157         FreeStrBuf(&NW->SpoolFileName);
00158         FreeStrBuf(&NW->tempFileName);
00159        FreeStrBuf(&NW->node);
00160        FreeStrBuf(&NW->host);
00161        FreeStrBuf(&NW->port);
00162        FreeStrBuf(&NW->secret);
00163        FreeStrBuf(&NW->Url);
00164        FreeStrBuf(&NW->IO.ErrMsg);
00165        FreeAsyncIOContents(&NW->IO);
00166        if (NW->HostLookup.VParsedDNSReply != NULL) {
00167               NW->HostLookup.DNSReplyFree(NW->HostLookup.VParsedDNSReply);
00168               NW->HostLookup.VParsedDNSReply = NULL;
00169        }
00170        free(NW);
00171 }
00172 
00173 #define NWC_DBG_SEND() EVN_syslog(LOG_DEBUG, ": > %s", ChrPtr(NW->IO.SendBuf.Buf))
00174 #define NWC_DBG_READ() EVN_syslog(LOG_DEBUG, ": < %s\n", ChrPtr(NW->IO.IOBuf))
00175 #define NWC_OK (strncasecmp(ChrPtr(NW->IO.IOBuf), "+OK", 3) == 0)
00176 
00177 eNextState FinalizeNetworker(AsyncIO *IO)
00178 {
00179        AsyncNetworker *NW = (AsyncNetworker *)IO->Data;
00180 
00181        network_talking_to(SKEY(NW->node), NTT_REMOVE);
00182 
00183        DeleteNetworker(IO->Data);
00184        return eAbort;
00185 }
00186 
00187 eNextState NWC_ReadGreeting(AsyncNetworker *NW)
00188 {
00189        char connected_to[SIZ];
00190        AsyncIO *IO = &NW->IO;
00191        NWC_DBG_READ();
00192        /* Read the server greeting */
00193        /* Check that the remote is who we think it is and warn the Aide if not */
00194        extract_token (connected_to, ChrPtr(NW->IO.IOBuf), 1, ' ', sizeof connected_to);
00195        if (strcmp(connected_to, ChrPtr(NW->node)) != 0)
00196        {
00197               if (NW->IO.ErrMsg == NULL)
00198                      NW->IO.ErrMsg = NewStrBuf();
00199               StrBufPrintf(NW->IO.ErrMsg,
00200                           "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
00201                           connected_to, ChrPtr(NW->node));
00202               EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
00203               CtdlAideMessage(ChrPtr(NW->IO.ErrMsg), "Network error");
00204               return eAbort;
00205        }
00206        return eSendReply;
00207 }
00208 
00209 eNextState NWC_SendAuth(AsyncNetworker *NW)
00210 {
00211        AsyncIO *IO = &NW->IO;
00212        /* We're talking to the correct node.  Now identify ourselves. */
00213        StrBufPrintf(NW->IO.SendBuf.Buf, "NETP %s|%s\n", 
00214                    config.c_nodename, 
00215                    ChrPtr(NW->secret));
00216        NWC_DBG_SEND();
00217        return eSendReply;
00218 }
00219 
00220 eNextState NWC_ReadAuthReply(AsyncNetworker *NW)
00221 {
00222        AsyncIO *IO = &NW->IO;
00223        NWC_DBG_READ();
00224        if (ChrPtr(NW->IO.IOBuf)[0] == '2')
00225        {
00226               return eSendReply;
00227        }
00228        else
00229        {
00230               if (NW->IO.ErrMsg == NULL)
00231                      NW->IO.ErrMsg = NewStrBuf();
00232               StrBufPrintf(NW->IO.ErrMsg,
00233                           "Connected to node \"%s\" but my secret wasn't accurate.",
00234                           ChrPtr(NW->node));
00235               EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
00236               CtdlAideMessage(ChrPtr(NW->IO.ErrMsg), "Network error");
00237               
00238               return eAbort;
00239        }
00240 }
00241 
00242 eNextState NWC_SendNDOP(AsyncNetworker *NW)
00243 {
00244        AsyncIO *IO = &NW->IO;
00245        NW->tempFileName = NewStrBuf();
00246        NW->SpoolFileName = NewStrBuf();
00247        StrBufPrintf(NW->SpoolFileName,
00248                    "%s/%s.%lx%x",
00249                    ctdl_netin_dir,
00250                    ChrPtr(NW->node),
00251                    time(NULL),// TODO: get time from libev
00252                    rand());
00253        StrBufStripSlashes(NW->SpoolFileName, 1);
00254        StrBufPrintf(NW->tempFileName, 
00255                    "%s/%s.%lx%x",
00256                    ctdl_nettmp_dir,
00257                    ChrPtr(NW->node),
00258                    time(NULL),// TODO: get time from libev
00259                    rand());
00260        StrBufStripSlashes(NW->tempFileName, 1);
00261        /* We're talking to the correct node.  Now identify ourselves. */
00262        StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NDOP\n"));
00263        NWC_DBG_SEND();
00264        return eSendReply;
00265 }
00266 
00267 eNextState NWC_ReadNDOPReply(AsyncNetworker *NW)
00268 {
00269        AsyncIO *IO = &NW->IO;
00270        int TotalSendSize;
00271        NWC_DBG_READ();
00272        if (ChrPtr(NW->IO.IOBuf)[0] == '2')
00273        {
00274 
00275               NW->IO.IOB.TotalSentAlready = 0;
00276               TotalSendSize = atol (ChrPtr(NW->IO.IOBuf) + 4);
00277               EVN_syslog(LOG_DEBUG, "Expecting to transfer %d bytes\n", TotalSendSize);
00278               if (TotalSendSize <= 0) {
00279                      NW->State = eNUOP - 1;
00280               }
00281               else {
00282                      int fd;
00283                      fd = open(ChrPtr(NW->tempFileName), 
00284                               O_EXCL|O_CREAT|O_NONBLOCK|O_WRONLY, 
00285                               S_IRUSR|S_IWUSR);
00286                      if (fd < 0)
00287                      {
00288                             EVN_syslog(LOG_CRIT,
00289                                    "cannot open %s: %s\n", 
00290                                    ChrPtr(NW->tempFileName), 
00291                                    strerror(errno));
00292 
00293                             NW->State = eQUIT - 1;
00294                             return eAbort;
00295                      }
00296                      FDIOBufferInit(&NW->IO.IOB, &NW->IO.RecvBuf, fd, TotalSendSize);
00297               }
00298               return eSendReply;
00299        }
00300        else
00301        {
00302               return eAbort;
00303        }
00304 }
00305 
00306 eNextState NWC_SendREAD(AsyncNetworker *NW)
00307 {
00308        AsyncIO *IO = &NW->IO;
00309        eNextState rc;
00310 
00311        if (NW->IO.IOB.TotalSentAlready < NW->IO.IOB.TotalSendSize)
00312        {
00313               /*
00314                * If shutting down we can exit here and unlink the temp file.
00315                * this shouldn't loose us any messages.
00316                */
00317               if (server_shutting_down)
00318               {
00319                      FDIOBufferDelete(&NW->IO.IOB);
00320                      unlink(ChrPtr(NW->tempFileName));
00321                      FDIOBufferDelete(&IO->IOB);
00322                      return eAbort;
00323               }
00324               StrBufPrintf(NW->IO.SendBuf.Buf, "READ %ld|%ld\n",
00325                           NW->IO.IOB.TotalSentAlready,
00326                           NW->IO.IOB.TotalSendSize);
00327 /*
00328                           ((NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready > IGNET_PACKET_SIZE)
00329                            ? IGNET_PACKET_SIZE : 
00330                            (NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready))
00331                      );
00332 */
00333               NWC_DBG_SEND();
00334               return eSendReply;
00335        }
00336        else 
00337        {
00338               NW->State = eCLOS;
00339               rc = NWC_DispatchWriteDone(&NW->IO);
00340               NWC_DBG_SEND();
00341 
00342               return rc;
00343        }
00344 }
00345 
00346 eNextState NWC_ReadREADState(AsyncNetworker *NW)
00347 {
00348        AsyncIO *IO = &NW->IO;
00349        NWC_DBG_READ();
00350        if (ChrPtr(NW->IO.IOBuf)[0] == '6')
00351        {
00352               NW->IO.IOB.ChunkSendRemain = 
00353                      NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
00354               return eReadFile;
00355        }
00356        FDIOBufferDelete(&IO->IOB);
00357        return eAbort;
00358 }
00359 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW);
00360 eNextState NWC_ReadREADBlob(AsyncNetworker *NW)
00361 {
00362        eNextState rc;
00363        AsyncIO *IO = &NW->IO;
00364        NWC_DBG_READ();
00365        if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
00366        {
00367               NW->State ++;
00368 
00369               FDIOBufferDelete(&NW->IO.IOB);
00370 
00371               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
00372                      EVN_syslog(LOG_ALERT, 
00373                             "Could not link %s to %s: %s\n",
00374                             ChrPtr(NW->tempFileName), 
00375                             ChrPtr(NW->SpoolFileName), 
00376                             strerror(errno));
00377               }
00378        
00379               unlink(ChrPtr(NW->tempFileName));
00380               rc = NWC_DispatchWriteDone(&NW->IO);
00381               NW->State --;
00382               return rc;
00383        }
00384        else {
00385               NW->State --;
00386               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
00387               return eSendReply; //NWC_DispatchWriteDone(&NW->IO);
00388        }
00389 }
00390 
00391 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW)
00392 {
00393        eNextState rc;
00394        AsyncIO *IO = &NW->IO;
00395 /* we don't have any data to debug print here. */
00396        if (NW->IO.IOB.TotalSentAlready >= NW->IO.IOB.TotalSendSize)
00397        {
00398               NW->State ++;
00399 
00400               FDIOBufferDelete(&NW->IO.IOB);
00401               if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
00402                      EVN_syslog(LOG_ALERT, 
00403                             "Could not link %s to %s: %s\n",
00404                             ChrPtr(NW->tempFileName), 
00405                             ChrPtr(NW->SpoolFileName), 
00406                             strerror(errno));
00407               }
00408        
00409               unlink(ChrPtr(NW->tempFileName));
00410               rc = NWC_DispatchWriteDone(&NW->IO);
00411               NW->State --;
00412               return rc;
00413        }
00414        else {
00415               NW->State --;
00416               NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
00417               return NWC_DispatchWriteDone(&NW->IO);
00418        }
00419 }
00420 eNextState NWC_SendCLOS(AsyncNetworker *NW)
00421 {
00422        AsyncIO *IO = &NW->IO;
00423        StrBufPlain(NW->IO.SendBuf.Buf, HKEY("CLOS\n"));
00424        NWC_DBG_SEND();
00425        return eSendReply;
00426 }
00427 
00428 eNextState NWC_ReadCLOSReply(AsyncNetworker *NW)
00429 {
00430        AsyncIO *IO = &NW->IO;
00431        NWC_DBG_READ();
00432        FDIOBufferDelete(&IO->IOB);
00433        if (ChrPtr(NW->IO.IOBuf)[0] != '2')
00434               return eTerminateConnection;
00435        return eSendReply;
00436 }
00437 
00438 
00439 eNextState NWC_SendNUOP(AsyncNetworker *NW)
00440 {
00441        AsyncIO *IO = &NW->IO;
00442        eNextState rc;
00443        long TotalSendSize;
00444        struct stat statbuf;
00445        int fd;
00446 
00447        StrBufPrintf(NW->SpoolFileName,
00448                    "%s/%s",
00449                    ctdl_netout_dir,
00450                    ChrPtr(NW->node));
00451        StrBufStripSlashes(NW->SpoolFileName, 1);
00452 
00453        fd = open(ChrPtr(NW->SpoolFileName), O_EXCL|O_NONBLOCK|O_RDONLY);
00454        if (fd < 0) {
00455               if (errno != ENOENT) {
00456                      EVN_syslog(LOG_CRIT,
00457                             "cannot open %s: %s\n", 
00458                             ChrPtr(NW->SpoolFileName), 
00459                             strerror(errno));
00460               }
00461               NW->State = eQUIT;
00462               rc = NWC_SendQUIT(NW);
00463               NWC_DBG_SEND();
00464               return rc;
00465        }
00466 
00467        if (fstat(fd, &statbuf) == -1) {
00468               EVN_syslog(LOG_CRIT, "FSTAT FAILED %s [%s]--\n", 
00469                         ChrPtr(NW->SpoolFileName), 
00470                         strerror(errno));
00471               if (fd > 0) close(fd);
00472               return eAbort;
00473        }
00474        TotalSendSize = statbuf.st_size;
00475        if (TotalSendSize == 0) {
00476               EVNM_syslog(LOG_DEBUG,
00477                      "Nothing to send.\n");
00478               NW->State = eQUIT;
00479               rc = NWC_SendQUIT(NW);
00480               NWC_DBG_SEND();
00481               if (fd > 0) close(fd);
00482               return rc;
00483        }
00484        FDIOBufferInit(&NW->IO.IOB, &NW->IO.SendBuf, fd, TotalSendSize);
00485 
00486        StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NUOP\n"));
00487        NWC_DBG_SEND();
00488        return eSendReply;
00489 
00490 }
00491 eNextState NWC_ReadNUOPReply(AsyncNetworker *NW)
00492 {
00493        AsyncIO *IO = &NW->IO;
00494        NWC_DBG_READ();
00495        if (ChrPtr(NW->IO.IOBuf)[0] != '2') {
00496               FDIOBufferDelete(&IO->IOB);
00497               return eAbort;
00498        }
00499        return eSendReply;
00500 }
00501 
00502 eNextState NWC_SendWRIT(AsyncNetworker *NW)
00503 {
00504        AsyncIO *IO = &NW->IO;
00505        StrBufPrintf(NW->IO.SendBuf.Buf, "WRIT %ld\n", 
00506                    NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready);
00507        NWC_DBG_SEND();
00508        return eSendReply;
00509 }
00510 eNextState NWC_ReadWRITReply(AsyncNetworker *NW)
00511 {
00512        AsyncIO *IO = &NW->IO;
00513        NWC_DBG_READ();
00514        if (ChrPtr(NW->IO.IOBuf)[0] != '7')
00515        {
00516               FDIOBufferDelete(&IO->IOB);
00517               return eAbort;
00518        }
00519 
00520        NW->IO.IOB.ChunkSendRemain = 
00521               NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
00522        return eSendFile;
00523 }
00524 
00525 eNextState NWC_SendBlobDone(AsyncNetworker *NW)
00526 {
00527        AsyncIO *IO = &NW->IO;
00528        eNextState rc;
00529        if (NW->IO.IOB.TotalSentAlready >= IO->IOB.TotalSendSize)
00530        {
00531               NW->State ++;
00532 
00533               FDIOBufferDelete(&IO->IOB);
00534               rc =  NWC_DispatchWriteDone(IO);
00535               NW->State --;
00536               return rc;
00537        }
00538        else {
00539               NW->State --;
00540               IO->IOB.ChunkSendRemain = IO->IOB.ChunkSize;
00541               rc = NWC_DispatchWriteDone(IO);
00542               NW->State --;
00543               return rc;
00544        }
00545 }
00546 
00547 eNextState NWC_SendUCLS(AsyncNetworker *NW)
00548 {
00549        AsyncIO *IO = &NW->IO;
00550        StrBufPlain(NW->IO.SendBuf.Buf, HKEY("UCLS 1\n"));
00551        NWC_DBG_SEND();
00552        return eSendReply;
00553 
00554 }
00555 eNextState NWC_ReadUCLS(AsyncNetworker *NW)
00556 {
00557        AsyncIO *IO = &NW->IO;
00558        NWC_DBG_READ();
00559 
00560        EVN_syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", NW->IO.IOB.ChunkSize, ChrPtr(NW->node));
00561        if (ChrPtr(NW->IO.IOBuf)[0] == '2') {
00562               EVN_syslog(LOG_DEBUG, "Removing <%s>\n", ChrPtr(NW->SpoolFileName));
00563               unlink(ChrPtr(NW->SpoolFileName));
00564        }
00565        FDIOBufferDelete(&IO->IOB);
00566        return eSendReply;
00567 }
00568 
00569 eNextState NWC_SendQUIT(AsyncNetworker *NW)
00570 {
00571        AsyncIO *IO = &NW->IO;
00572        StrBufPlain(NW->IO.SendBuf.Buf, HKEY("QUIT\n"));
00573 
00574        NWC_DBG_SEND();
00575        return eSendReply;
00576 }
00577 
00578 eNextState NWC_ReadQUIT(AsyncNetworker *NW)
00579 {
00580        AsyncIO *IO = &NW->IO;
00581        NWC_DBG_READ();
00582 
00583        return eAbort;
00584 }
00585 
00586 
00587 NWClientHandler NWC_ReadHandlers[] = {
00588        NWC_ReadGreeting,
00589        NWC_ReadAuthReply,
00590        NWC_ReadNDOPReply,
00591        NWC_ReadREADState,
00592        NWC_ReadREADBlob,
00593        NWC_ReadCLOSReply,
00594        NWC_ReadNUOPReply,
00595        NWC_ReadWRITReply,
00596        NWC_SendBlobDone,
00597        NWC_ReadUCLS,
00598        NWC_ReadQUIT};
00599 
00600 long NWC_ConnTimeout = 100;
00601 
00602 const long NWC_SendTimeouts[] = {
00603        100,
00604        100,
00605        100,
00606        100,
00607        100,
00608        100,
00609        100,
00610        100
00611 };
00612 const ConstStr NWC[] = {
00613        {HKEY("Connection broken during ")},
00614        {HKEY("Connection broken during ")},
00615        {HKEY("Connection broken during ")},
00616        {HKEY("Connection broken during ")},
00617        {HKEY("Connection broken during ")},
00618        {HKEY("Connection broken during ")},
00619        {HKEY("Connection broken during ")},
00620        {HKEY("Connection broken during ")}
00621 };
00622 
00623 NWClientHandler NWC_SendHandlers[] = {
00624        NULL,
00625        NWC_SendAuth,
00626        NWC_SendNDOP,
00627        NWC_SendREAD,
00628        NWC_ReadREADBlobDone,
00629        NWC_SendCLOS,
00630        NWC_SendNUOP,
00631        NWC_SendWRIT,
00632        NWC_SendBlobDone,
00633        NWC_SendUCLS,
00634        NWC_SendQUIT
00635 };
00636 
00637 const long NWC_ReadTimeouts[] = {
00638        100,
00639        100,
00640        100,
00641        100,
00642        100,
00643        100,
00644        100,
00645        100,
00646        100,
00647        100
00648 };
00649 
00650 
00651 
00652 
00653 eNextState nwc_get_one_host_ip_done(AsyncIO *IO)
00654 {
00655        AsyncNetworker *NW = IO->Data;
00656        struct hostent *hostent;
00657 
00658        QueryCbDone(IO);
00659 
00660        hostent = NW->HostLookup.VParsedDNSReply;
00661        if ((NW->HostLookup.DNSStatus == ARES_SUCCESS) && 
00662            (hostent != NULL) ) {
00663               memset(&NW->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
00664               if (NW->IO.ConnectMe->IPv6) {
00665                      memcpy(&NW->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
00666                             &hostent->h_addr_list[0],
00667                             sizeof(struct in6_addr));
00668                      
00669                      NW->IO.ConnectMe->Addr.sin6_family = hostent->h_addrtype;
00670                      NW->IO.ConnectMe->Addr.sin6_port   = htons(atol(ChrPtr(NW->port)));
00671               }
00672               else {
00673                      struct sockaddr_in *addr = (struct sockaddr_in*) &NW->IO.ConnectMe->Addr;
00674                      /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
00675 //                   addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
00676                      memcpy(&addr->sin_addr.s_addr, 
00677                             hostent->h_addr_list[0], 
00678                             sizeof(uint32_t));
00679                      
00680                      addr->sin_family = hostent->h_addrtype;
00681                      addr->sin_port   = htons(504);
00682                      
00683               }
00684               return nwc_connect_ip(IO);
00685        }
00686        else
00687               return eAbort;
00688 }
00689 
00690 
00691 eNextState nwc_get_one_host_ip(AsyncIO *IO)
00692 {
00693        AsyncNetworker *NW = IO->Data;
00694        /* 
00695         * here we start with the lookup of one host.
00696         */ 
00697 
00698        EVN_syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
00699 
00700        EVN_syslog(LOG_DEBUG, 
00701                  "NWC client[%ld]: looking up %s-Record %s : %d ...\n", 
00702                  NW->n, 
00703                  (NW->IO.ConnectMe->IPv6)? "aaaa": "a",
00704                  NW->IO.ConnectMe->Host, 
00705                  NW->IO.ConnectMe->Port);
00706 
00707        QueueQuery((NW->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a, 
00708                  NW->IO.ConnectMe->Host, 
00709                  &NW->IO, 
00710                  &NW->HostLookup, 
00711                  nwc_get_one_host_ip_done);
00712        IO->NextState = eReadDNSReply;
00713        return IO->NextState;
00714 }
00718 eReadState NWC_ReadServerStatus(AsyncIO *IO)
00719 {
00720 //     AsyncNetworker *NW = IO->Data;
00721        eReadState Finished = eBufferNotEmpty; 
00722 
00723        switch (IO->NextState) {
00724        case eSendDNSQuery:
00725        case eReadDNSReply:
00726        case eDBQuery:
00727        case eConnect:
00728        case eTerminateConnection:
00729        case eAbort:
00730               Finished = eReadFail;
00731               break;
00732        case eSendReply: 
00733        case eSendMore:
00734        case eReadMore:
00735        case eReadMessage: 
00736               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
00737               break;
00738        case eReadFile:
00739        case eSendFile:
00740        case eReadPayload:
00741               break;
00742        }
00743        return Finished;
00744 }
00745 
00746 
00747 
00748 eNextState NWC_FailNetworkConnection(AsyncIO *IO)
00749 {
00750        return eAbort;
00751 }
00752 
00753 void NWC_SetTimeout(eNextState NextTCPState, AsyncNetworker *NW)
00754 {
00755        AsyncIO *IO = &NW->IO;
00756        double Timeout = 0.0;
00757 
00758        EVN_syslog(LOG_DEBUG, "%s - %d\n", __FUNCTION__, NextTCPState);
00759 
00760        switch (NextTCPState) {
00761        case eSendReply:
00762        case eSendMore:
00763               break;
00764        case eReadFile:
00765        case eReadMessage:
00766               Timeout = NWC_ReadTimeouts[NW->State];
00767               break;
00768        case eReadPayload:
00769               Timeout = 100000;
00770               /* TODO!!! */
00771               break;
00772        case eSendDNSQuery:
00773        case eReadDNSReply:
00774        case eConnect:
00775        case eSendFile:
00776 //TODO
00777        case eTerminateConnection:
00778        case eDBQuery:
00779        case eAbort:
00780        case eReadMore:
00781               return;
00782        }
00783        if (Timeout > 0) {
00784               EVN_syslog(LOG_DEBUG, 
00785                         "%s - %d %f\n",
00786                         __FUNCTION__,
00787                         NextTCPState,
00788                         Timeout);
00789               SetNextTimeout(&NW->IO, Timeout*100);
00790        }
00791 }
00792 
00793 
00794 eNextState NWC_DispatchReadDone(AsyncIO *IO)
00795 {
00796        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00797        AsyncNetworker *NW = IO->Data;
00798        eNextState rc;
00799 
00800        rc = NWC_ReadHandlers[NW->State](NW);
00801        if (rc != eReadMore)
00802               NW->State++;
00803        NWC_SetTimeout(rc, NW);
00804        return rc;
00805 }
00806 eNextState NWC_DispatchWriteDone(AsyncIO *IO)
00807 {
00808        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00809        AsyncNetworker *NW = IO->Data;
00810        eNextState rc;
00811 
00812        rc = NWC_SendHandlers[NW->State](NW);
00813        NWC_SetTimeout(rc, NW);
00814        return rc;
00815 }
00816 
00817 /*****************************************************************************/
00818 /*                     Networker CLIENT ERROR CATCHERS                       */
00819 /*****************************************************************************/
00820 eNextState NWC_Terminate(AsyncIO *IO)
00821 {
00822        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00823        FinalizeNetworker(IO);
00824        return eAbort;
00825 }
00826 
00827 eNextState NWC_TerminateDB(AsyncIO *IO)
00828 {
00829        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00830        FinalizeNetworker(IO);
00831        return eAbort;
00832 }
00833 
00834 eNextState NWC_Timeout(AsyncIO *IO)
00835 {
00836        AsyncNetworker *NW = IO->Data;
00837        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00838 
00839        if (NW->IO.ErrMsg == NULL)
00840               NW->IO.ErrMsg = NewStrBuf();
00841        StrBufPrintf(NW->IO.ErrMsg, "Timeout while talking to %s \r\n", ChrPtr(NW->host));
00842        return NWC_FailNetworkConnection(IO);
00843 }
00844 eNextState NWC_ConnFail(AsyncIO *IO)
00845 {
00846        AsyncNetworker *NW = IO->Data;
00847 
00848        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00849        if (NW->IO.ErrMsg == NULL)
00850               NW->IO.ErrMsg = NewStrBuf();
00851        StrBufPrintf(NW->IO.ErrMsg, "failed to connect %s \r\n", ChrPtr(NW->host));
00852 
00853        return NWC_FailNetworkConnection(IO);
00854 }
00855 eNextState NWC_DNSFail(AsyncIO *IO)
00856 {
00857        AsyncNetworker *NW = IO->Data;
00858 
00859        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00860        if (NW->IO.ErrMsg == NULL)
00861               NW->IO.ErrMsg = NewStrBuf();
00862        StrBufPrintf(NW->IO.ErrMsg, "failed to look up %s \r\n", ChrPtr(NW->host));
00863 
00864        return NWC_FailNetworkConnection(IO);
00865 }
00866 eNextState NWC_Shutdown(AsyncIO *IO)
00867 {
00868        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00869 
00870        FinalizeNetworker(IO);
00871        return eAbort;
00872 }
00873 
00874 
00875 eNextState nwc_connect_ip(AsyncIO *IO)
00876 {
00877        AsyncNetworker *NW = IO->Data;
00878 
00879        EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00880        EVN_syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", 
00881                  ChrPtr(NW->node), 
00882                  ChrPtr(NW->host),
00883                  ChrPtr(NW->port));
00884        
00885        return EvConnectSock(IO,
00886                           NWC_ConnTimeout,
00887                           NWC_ReadTimeouts[0],
00888                           1);
00889 }
00890 
00891 static int NetworkerCount = 0;
00892 void RunNetworker(AsyncNetworker *NW)
00893 {
00894        NW->n = NetworkerCount++;
00895        network_talking_to(SKEY(NW->node), NTT_ADD);
00896        syslog(LOG_DEBUG, "NW[%s][%ld]: polling\n", ChrPtr(NW->node), NW->n);
00897        ParseURL(&NW->IO.ConnectMe, NW->Url, 504);
00898 
00899        InitIOStruct(&NW->IO,
00900                    NW,
00901                    eReadMessage,
00902                    NWC_ReadServerStatus,
00903                    NWC_DNSFail,
00904                    NWC_DispatchWriteDone,
00905                    NWC_DispatchReadDone,
00906                    NWC_Terminate,
00907                    NWC_TerminateDB,
00908                    NWC_ConnFail,
00909                    NWC_Timeout,
00910                    NWC_Shutdown);
00911 
00912        safestrncpy(((CitContext *)NW->IO.CitContext)->cs_host, 
00913                   ChrPtr(NW->host),
00914                   sizeof(((CitContext *)NW->IO.CitContext)->cs_host)); 
00915 
00916        if (NW->IO.ConnectMe->IsIP) {
00917               QueueEventContext(&NW->IO,
00918                               nwc_connect_ip);
00919        }
00920        else { /* uneducated admin has chosen to add DNS to the equation... */
00921               QueueEventContext(&NW->IO,
00922                               nwc_get_one_host_ip);
00923        }
00924 }
00925 
00926 /*
00927  * Poll other Citadel nodes and transfer inbound/outbound network data.
00928  * Set "full" to nonzero to force a poll of every node, or to zero to poll
00929  * only nodes to which we have data to send.
00930  */
00931 void network_poll_other_citadel_nodes(int full_poll, HashList *ignetcfg)
00932 {
00933        const char *key;
00934        long len;
00935        HashPos *Pos;
00936        void *vCfg;
00937        AsyncNetworker *NW;
00938        StrBuf *SpoolFileName;
00939        
00940        int poll = 0;
00941        
00942        if (GetCount(ignetcfg) ==0) {
00943               syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
00944               return;
00945        }
00946        become_session(&networker_client_CC);
00947 
00948        SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
00949 
00950        Pos = GetNewHashPos(ignetcfg, 0);
00951 
00952        while (GetNextHashPos(ignetcfg, Pos, &len, &key, &vCfg))
00953        {
00954               /* Use the string tokenizer to grab one line at a time */
00955               if(server_shutting_down)
00956                      return;/* TODO free stuff*/
00957               NodeConf *pNode = (NodeConf*) vCfg;
00958               poll = 0;
00959               NW = (AsyncNetworker*)malloc(sizeof(AsyncNetworker));
00960               memset(NW, 0, sizeof(AsyncNetworker));
00961               
00962               NW->node = NewStrBufDup(pNode->NodeName);
00963               NW->host = NewStrBufDup(pNode->Host);
00964               NW->port = NewStrBufDup(pNode->Port);
00965               NW->secret = NewStrBufDup(pNode->Secret);
00966               
00967               if ( (StrLength(NW->node) != 0) && 
00968                    (StrLength(NW->secret) != 0) &&
00969                    (StrLength(NW->host) != 0) &&
00970                    (StrLength(NW->port) != 0))
00971               {
00972                      poll = full_poll;
00973                      if (poll == 0)
00974                      {
00975                             StrBufAppendBufPlain(SpoolFileName, HKEY("/"), 0);
00976                             StrBufAppendBuf(SpoolFileName, NW->node, 0);
00977                             StrBufStripSlashes(SpoolFileName, 1);
00978                             
00979                             if (access(ChrPtr(SpoolFileName), R_OK) == 0) {
00980                                    poll = 1;
00981                             }
00982                      }
00983               }
00984               if (poll && 
00985                   (StrLength(NW->host) > 0) && 
00986                   strcmp("0.0.0.0", ChrPtr(NW->host)))
00987               {
00988                      NW->Url = NewStrBuf();
00989                      StrBufPrintf(NW->Url, "citadel://:%s@%s:%s", 
00990                                  ChrPtr(NW->secret),
00991                                  ChrPtr(NW->host),
00992                                  ChrPtr(NW->port));
00993                      if (!network_talking_to(SKEY(NW->node), NTT_CHECK))
00994                      {
00995                             RunNetworker(NW);
00996                             continue;
00997                      }
00998               }
00999               DeleteNetworker(NW);
01000        }
01001        FreeStrBuf(&SpoolFileName);
01002        DeleteHashPos(&Pos);
01003 }
01004 
01005 
01006 void network_do_clientqueue(void)
01007 {
01008        HashList *working_ignetcfg;
01009        int full_processing = 1;
01010        static time_t last_run = 0L;
01011 
01012        /*
01013         * Run the full set of processing tasks no more frequently
01014         * than once every n seconds
01015         */
01016        if ( (time(NULL) - last_run) < config.c_net_freq ) {
01017               full_processing = 0;
01018               syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
01019                      config.c_net_freq - (time(NULL)- last_run)
01020               );
01021        }
01022 
01023        working_ignetcfg = load_ignetcfg();
01024        /*
01025         * Poll other Citadel nodes.  Maybe.  If "full_processing" is set
01026         * then we poll everyone.  Otherwise we only poll nodes we have stuff
01027         * to send to.
01028         */
01029        network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
01030        DeleteHash(&working_ignetcfg);
01031 }
01032 
01033 void LogDebugEnableNetworkClient(const int n)
01034 {
01035        NetworkClientDebugEnabled = n;
01036 }
01037 /*
01038  * Module entry point
01039  */
01040 CTDL_MODULE_INIT(network_client)
01041 {
01042        if (!threading)
01043        {
01044               CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
01045               
01046               CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER, PRIO_SEND + 10);
01047               CtdlRegisterDebugFlagHook(HKEY("networkclient"), LogDebugEnableNetworkClient, &NetworkClientDebugEnabled);
01048 
01049        }
01050        return "networkclient";
01051 }