1 /* Copyright (C) 2002 GFRN systems
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License as
5    published by the Free Software Foundation; either version 2 of the
6    License, or (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful, but
9    WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11    See the GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16    02111-1307, USA.
17 
18    The latest version of this program may be found at
19    http://CQiNet.sourceforge.net
20 
21    $Log: dirclient.c,v $
22    Revision 1.25  2008/09/14 17:04:51  wb6ymh
23    Corrected a memory leak introduced in 0.84 that occured when an existing
24    station's status was updated. ParseStationList now frees the previous
25    copy of the users QTH before updating it.
26 
27    Revision 1.24  2008/02/26 17:38:15  wb6ymh
28    1. Added dmalloc support.
29    2. Modified ParseStationList to log messages received from the Echolink
30       directory server.
31    3. Added DirCleanup.
32 
33    Revision 1.23  2008/02/09 16:55:21  wb6ymh
34    Added support for a new configuration variable ShowStatusInInfo.
35 
36    Revision 1.22  2007/06/29 18:16:16  wb6ymh
37    Added QthInHostsFile configuration variable to allow Qth information to
38    be optionally included in the host file.  NB: As of today the inclusion
39    of Qth information breaks the EchoIRLP scripts.
40 
41    Revision 1.21  2007/06/27 19:26:25  wb6ymh
42    Enhancements from Scott KI4LKF:
43    1. Modified ParseStationList to save the user's QTH in UserInfo structure.
44    2. Modified CleanUserList to save node QTH in host file.
45 
46    Revision 1.20  2007/04/27 14:29:02  wb6ymh
47    Added FileUserByNodeID.
48 
49    Revision 1.19  2006/08/05 23:30:04  wb6ymh
50    Corrected warnings and errors with GCC 4.x.x.
51 
52    Revision 1.18  2004/11/29 00:31:31  wb6ymh
53    1. Added (off line) to comment to mark inactive stations in host file.
54    2. Corrected number of stations listed in debug message for a full station
55       list download.
56 
57    Revision 1.17  2004/11/27 22:09:26  wb6ymh
58    1. Modified version reporting to use bEchoIrlpMode flag.
59    2. Changed EOL convention back to Unix sytle(!).
60 
61    Revision 1.16  2004/11/22 00:52:56  wb6ymh
62    1. Detect EchoIRLP by SF_Port != SF_ReplyPort instead of SF_ReplyPort !=
63       SF_AUDIO_PORT.  Allows conferences to use nonstandard ports without
64       triggering an EchoIRLP version string.
65    2. Removed broken, buggy ClientIPAdrUpdate stuff.  If you ISP forces you to
66       change your IP address all of the time, CHANGE YOUR ISP !
67    3. Added support for the IncludeAllHosts configuration variable.
68 
69    Revision 1.15  2004/05/29 17:25:22  wb6ymh
70    1. Added code to update the IP address of persistent clients when the client's
71       IP address changes in the directory.
72 
73    2. Corrected memory leak created when compressed directory support was added.
74 
75    3. Changed version string sent to EchoLink directory servers from v.vvB to
76       v.vvI when tbd is used with the EchoIRLP project.
77 
78    4. Eliminated unused and unneeded fields from the UserInfo structure to reduce
79       memory requirements.
80 
81    Revision 1.14  2003/08/31 23:21:03  wb6ymh
82    1. Added support for compressed and differential station lists.
83    2. Corrected portability problems that occured when u_long was assumed to
84       be the same size as an IP address.
85    3. Added code to clear sockaddr_in before attempting to bind. If the structure
86       is not cleared bind will fail on FreeBSD from time to time.
87    4. Corrected a bug that prevented thebridge from operating properly on NT 4.0.
88       Thanks to K1RFD for the fix and N4LED for reporting the bug and testing
89       the fix.
90    5. Added the EmailAdr to information provided to directory server on login.
91    6. Added calls to DoConfigPass() at end of first station list download and
92       second successful directory server login.
93 
94    Revision 1.13  2003/04/30 21:52:58  wb6ymh
95    1. Added support for Bind2IP configuration variable to ServerRequest().
96    2. Added code to cause an exit if the directory server returns "NO" to login
97       attempt.
98 
99    Revision 1.12  2003/01/05 17:05:02  wb6ymh
100    1. Report real version number to directory server on login instead of
101       old iLink client string.
102    2. Changed logout string from "OFF-" to "OFF-V".
103    3. Modified CheckCallResp() to add authorized clients only, unauthorized
104       clients are simply ignored. (This change was lost before the last
105       release and was not actually present in 0.45).
106 
107    Revision 1.11  2003/01/01 19:12:40  wb6ymh
108    1.Added 30 second timeout to all directory server requests.
109    2.Corrected infinite loop in PullerLoginAck which occurred when read()
110      returned an error.
111    3.Modified CheckCallResp() to add authorized clients only, unauthorized
112      clients are simply ignored.
113 
114    Revision 1.10  2002/12/21 18:14:09  wb6ymh
115    Added code to generate *nix style host file containing active EchoLink
116    users.
117 
118    Revision 1.9  2002/12/18 00:59:51  wb6ymh
119    Removed extra call to DescrambleIP(), added newlines to several log messages.
120 
121    Revision 1.8  2002/11/02 18:23:21  wb6ymh
122    1. Added ValidateCallsign() routine to check if a  specified user is logged
123       in and has a vaild callsign.
124    2. Modified to call new caching GetHostByName() instead of library function
125       gethostbyname().
126    3. Modified code to list conference in directory as "Private conference"
127       when the PrivateConference flag is set.
128    4. Modified CleanUserList() to keep entries for stations that have logged
129       out for InactiveDirTimeout seconds. This reduces the number of times we
130       need to call ValidateCallsign() when a user connects right after starting
131       EchoLink.
132    5. Added code to log DeScrambleIP() failures.
133 
134    Revision 1.7  2002/10/25 14:28:52  wb6ymh
135    Added support for iLink directory servers.
136 
137    Revision 1.6  2002/09/14 17:09:25  wb6ymh
138    Added code to save the conference's node id in OurNodeID in *Network* order.
139    Corrected bug in the message logged when an attempt to resolve the directory
140    server's IP address failed.
141 
142    Revision 1.5  2002/09/02 15:24:36  wb6ymh
143    Reworked (yet again) recovery from hung server request by clearing
144    bInClientTree before calling DeleteClient().  Moved code that starts second
145    half of two step requests into PullerCleanup(), because DeleteClient() no
146    longer allows the current client to be deleted until the mainloop is done
147    with it.
148 
149    Revision 1.4  2002/09/01 15:52:51  wb6ymh
150    Modified ServerRequest() to avl_replace() new clients rather than avl_insert(),
151    avl_delete, avl_insert().  Deleting clients while the tree is being
152    transversed is potential death!  (It's ok to delete the current client
153    because the mainloop has already saved the index of the next client,
154    but if you delete what would be the next client, the mainloop will
155    try to service him anyway leading to a crash)
156 
157    Revision 1.3  2002/09/01 00:05:42  wb6ymh
158    1. Added support for multiple directory servers.
159    2. Modified all log entries to use %m for error message display when available.
160    3. Added code to increment request/success/failure counters in ServerStats.
161    4. Added code to track number of open sockets.
162    5. Modified code to delete any zombie clients that may have been created by
163       earlier calls to ServerRequest().
164    6. Modified code to wake on socket exception in addition to write. Windows
165       sets an exception when a connect() fails, Unix sets write.  This was
166       causing the Windows version to fail to detect failed connection attempts
167       which caused both memory and socket handles leaks.  Eventually this lead to
168       a crash after about 48 hours of operation.
169 
170    Revision 1.2  2002/08/12 17:10:47  wb6ymh
171    Made TimeNow a global.
172    Removed argument to GetTimeNow().
173 
174    Revision 1.1.1.1  2002/08/10 20:33:41  wb6ymh
175    initial import
176 
177 */
178 
179 #include "common.h"
180 
181 #ifndef _WIN32
182    // FreeBSD, Linux, etc..
183    #include <stdio.h>
184    #include <stdlib.h>
185    #include <sys/types.h>
186    #ifdef TIME_WITH_SYS_TIME
187       #include <sys/time.h>
188       #include <time.h>
189    #else
190       #ifdef HAVE_SYS_TIME_H
191          #include <sys/time.h>
192       #else
193          #include <time.h>
194       #endif
195    #endif
196    #include <unistd.h>
197    #include <sys/socket.h>
198    #include <netinet/in.h>
199    #include <arpa/inet.h>
200    #include <errno.h>
201    #include <string.h>
202    #include <ctype.h>
203 #ifdef HAVE_FCNTL_H
204    #include <fcntl.h>
205 #endif
206    #include <netdb.h>
207 #else
208    // Windoze
209    #include <stdio.h>
210    #include <time.h>
211    #include <io.h>
212    #include <winsock2.h>
213    #include <ctype.h>
214 #endif
215 
216 #include "avl.h"
217 #include "main.h"
218 #include "configvars.h"
219 #include "ilink.h"
220 #include "users.h"
221 #include "conference.h"
222 #include "dirclient.h"
223 #include "hostfile.h"
224 #include "eventhook.h"
225 #include <zlib.h>
226 #include "sf.h"
227 
228 #ifdef USE_DMALLOC
229 #include "dmalloc.h"
230 #endif
231 
232 #define  ECHOLINK_HOSTS_FILE     "hosts"
233 #define  ECHOLINK_HOSTS_TMP_FILE "hosts.temp"
234 
235 #define  SERVER_REQ_TO           30000 /* 30 seconds (in Milliseconds) */
236 
237 int DuplicateClientsDeleted = 0;
238 int PullerStatusBusy = 0;
239 ServerStats_t ServerStats[NUM_DIRECTORY_SERVERS];
240 unsigned long OurNodeID = 0;
241 int ActiveDirEntries = 0;
242 int InactiveDirEntries = 0;
243 char  SnapShortID[MAX_SNAPSHOT_LEN] = {'0',0};
244 
245 typedef struct {
246    SERVER_REQUEST Req;
247    char  Callsign[MAX_CALL_LEN+1];
248    char  Qth[MAX_QTH_LEN+1];
249    int   NodeID;
250    int   LocalHr;
251    int   LocalMin;
252    int   NewStations;
253    int   DeletedStations;
254    int   UpDatedStations;
255    int   Server;
256 
257    void *Arg;
258    z_stream    c_stream;
259    char  *ZBuf;      // Decompressed data buffer
260    int   ZCount;     // Amount of data in ZCount
261    int   ZRetCount;  // Amount of data consumed from ZBuf
262    int   ZBufSize;   // allocation size of ZBuf
263    int   StationCount;
264 
265    int   bIgnore:1;
266    int   bBusy:1;
267    int   bPreambleRead:1;
268    int   bCompressed:1;
269    int   bUnCompressed:1;
270    int   bAbort:1;
271    int   bComplete:1;
272    int   bCompressedEOF:1;
273    int   bDifferential:1;
274    int   bStationListEndFound:1;
275 } PullerInfo;
276 
277 PullerInfo *pPullerDebug = NULL;
278 
279 char *Req2String[] = {
280    "?",           // SERV_REQ_NONE,
281    "Login",       // SERV_REQ_LOGIN,
282    "Logout",      // SERV_REQ_LOGOUT,
283    "Login&List",  // SERV_REQ_LOGIN_AND_LIST
284    "StationList", // SERV_REQ_STATION_LIST
285    "Shutdown",    // SERV_REQ_SHUTDOWN
286    "CheckCall"    // SERV_REQ_CHECK_CALL
287 };
288 
289 int PullerLogin(ClientInfo *pClient);
290 int PullerLoginAck(ClientInfo *pClient);
291 int PullerConnected(ClientInfo *pClient);
292 int GetStationList(ClientInfo *pClient);
293 void ParseStationList(ClientInfo *pClient,char *Buf,int Count);
294 void CleanUserList(int bDifferential);
295 int CheckCall(ClientInfo *pClient);
296 int PullerHandler(ClientInfo *pPuller);
297 void PullerCleanup(ClientInfo *pPuller);
298 int ServerRequestComplete(ClientInfo *p);
299 void DeScrambleIP(char *IO);
300 void RightTrim(char *Buf);
301 int ReadStationData(ClientInfo *p);
302 
ValidateCallsign(char * Callsign,IPAdrUnion * pIP)303 void ValidateCallsign(char *Callsign,IPAdrUnion *pIP)
304 {
305    char  Temp[80];
306 
307    snprintf(Temp,sizeof(Temp),"%s\r%s\r",Callsign,inet_ntoa(pIP->i.sin_addr));
308    ServerRequest(SERV_REQ_CHECK_CALL,0,strdup(Temp));
309 }
310 
311 void DeScrambleIP(char *IO);
312 
ServerRequest(SERVER_REQUEST Req,int Server,void * Arg)313 void ServerRequest(SERVER_REQUEST Req,int Server,void *Arg)
314 {
315    int i;
316    ClientInfo *pClient;
317    ClientInfo *pClient1;
318    PullerInfo *pPuller;
319    PullerInfo *pPuller1;
320    struct hostent *pTemp;
321    IPAdrUnion MyAdr;
322 
323    ServerStats[Server].Requests++;
324    pClient = CreateNewClient();
325    if(pClient != NULL) {
326       pClient->Complete = ServerRequestComplete;
327       pPuller = malloc(sizeof(PullerInfo));
328       if(pPuller != NULL) {
329          pClient->p = pPuller;
330          pClient->Cleanup = PullerCleanup;
331          memset(pPuller,0,sizeof(PullerInfo));
332          pPuller->Server = Server;
333          pPuller->Req = Req;
334          pPuller->Arg = Arg;
335 
336          pClient->Socket = socket(AF_INET,SOCK_STREAM,0);
337          if(pClient->Socket == SOCKET_ERROR) {
338             LOG_ERROR(("ServerRequest(): Socket() failed %s",
339                        Err2String(ERROR_CODE)));
340             DeleteClient(pClient);
341             pClient = NULL;
342          }
343          else {
344             OpenSockets++;
345          }
346       }
347    }
348 
349    if(pClient != NULL && Bind2IP != NULL) {
350       memset(&MyAdr,0,sizeof(MyAdr));
351       MyAdr.i.sin_family = AF_INET;
352       MyAdr.PORT = 0;      // Any port
353       MyAdr.ADDR = inet_addr(Bind2IP);
354       if(MyAdr.ADDR == INADDR_NONE) {
355          LOG_ERROR(("ServerRequest(): failed to convert \"%s\" to IP address.\n",
356                     Bind2IP));
357          DeleteClient(pClient);
358          pClient = NULL;
359       }
360       else if(bind(pClient->Socket,&MyAdr.s,sizeof(MyAdr)) == SOCKET_ERROR) {
361          LOG_ERROR(("ServerRequest(): bind() failed, %s",
362                     Err2String(ERROR_CODE)));
363          DeleteClient(pClient);
364          pClient = NULL;
365       }
366    }
367 
368    if(pClient != NULL) {
369    // Set the socket to non-blocking
370    #ifdef _WIN32
371       i = 1;
372       ioctlsocket(pClient->Socket,FIONBIO,&i);
373    #else
374       fcntl(pClient->Socket,F_SETFL,O_NONBLOCK);
375    #endif
376 
377       pClient->HisAdr.i.sin_family = AF_INET;
378       pClient->HisAdr.i.sin_port = htons(ILINK_DIRSERVER_PORT);
379 
380       pTemp = GetHostByName(DirServerHost[Server]);
381       if(pTemp != NULL) {
382          pClient->HisAdr.i.sin_addr.s_addr = IP_FROM_HOSTENT(pTemp,0);
383       }
384       else {
385       // That didn't work.  On some stacks a numeric IP address
386       // will not parse with gethostbyname.  Try to convert it as a
387       // numeric address
388          pClient->HisAdr.i.sin_addr.s_addr = inet_addr(DirServerHost[Server]);
389       }
390 
391       if(pClient->HisAdr.i.sin_addr.s_addr == INADDR_NONE) {
392          LOG_ERROR(("ServerRequest(): Couldn't convert \"%s\" to an IP address.\n",
393                   DirServerHost[Server]));
394          DeleteClient(pClient);
395          pClient = NULL;
396       }
397       else {
398          if((pClient1 = avl_replace(ClientTree,pClient)) != NULL) {
399             pClient1->bInClientTree = FALSE;
400             DuplicateClientsDeleted++;
401             pPuller1 = (PullerInfo *) pClient1->p;
402             if(pPuller1 != NULL) {
403                ServerStats[pPuller1->Server].Failure++;
404             }
405             pClient1->Err = ERR_SERVER_TIMEOUT;
406             LOG_ERROR(("ServerRequest(): Duplicate client removed from tree.\n"));
407             DeleteClient(pClient1);
408          }
409          pClient->bInClientTree = TRUE;
410       }
411    }
412 
413    if(pClient != NULL) {
414       pClient->State = PullerConnected;
415       pClient->NextState = PullerLogin;
416 
417       i = connect(pClient->Socket,&pClient->HisAdr.s,sizeof(pClient->HisAdr));
418 
419       if(i == SOCKET_ERROR) {
420          if(ERROR_CODE == ERROR_IN_PROGRESS) {
421          // The expected result, connect takes a while
422             D2PRINTF(("ServerRequest(), connect() returned ERROR_IN_PROGRESS.\n"));
423          // Wait for up to SERVER_REQ_TO millseconds for the connection
424             SetTimeout(pClient,SERVER_REQ_TO);
425             SET_WAIT4_CONNECT(pClient);
426          }
427          else {
428          // Some kind of error other than in progress
429             LOG_ERROR(("ServerRequest(), connect() failed, %s",
430                        Err2String(ERROR_CODE)));
431             OpComplete(pClient,ERROR_CODE);
432          }
433       }
434       else {
435       // That certainly was quick !
436          LOG_WARN(("ServerRequest(), connect() didn't return an error(!).\n"));
437          PullerConnected(pClient);
438       }
439    }
440 }
441 
FindUserByNodeID(int NodeID)442 UserInfo *FindUserByNodeID(int NodeID)
443 {
444    struct avl_traverser avl_trans;
445    UserInfo *pUser;
446 
447    pUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
448    while(pUser != NULL) {
449       if(pUser->NodeID == NodeID) {
450          break;
451       }
452       pUser = (UserInfo *) avl_t_next(&avl_trans);
453    }
454 
455    return pUser;
456 }
457 
PullerConnected(ClientInfo * pClient)458 int PullerConnected(ClientInfo *pClient)
459 {
460    int i;
461    int Err = 0;
462    PullerInfo *pPuller = (PullerInfo *) pClient->p;
463    int Ret = FALSE;
464 
465    if(pClient->x == 0 && pClient->bTimeOut) {
466       i = 1;
467       Err = ERR_SERVER_TIMEOUT;
468       LOG_WARN(("PullerConnected(): connect() timed out for client %u\n",
469                 pClient->SN));
470    }
471    else {
472       i = connect(pClient->Socket,&pClient->HisAdr.s,sizeof(pClient->HisAdr));
473 
474       if(i != 0 && (Err = ERROR_CODE) != ERROR_CONNECTED)
475       // Connection failed.
476 #ifndef _WIN32
477       {
478          LOG_WARN(("PullerConnected(): connect() failed for client %u %s",
479                    pClient->SN,Err2String(Err)));
480       }
481    }
482 
483 #else
484       {
485       // Heavy sigh...
486       // Apparently there are bugs in Microsoft's TCP/IP stacks (shock !)
487       // that causes connect() to return an error other than WSAEISCONN even
488       // after select() has indicated that the socket is ready for write.
489       // Try it a few times before giving up.
490 
491          if(pClient->x > 0) {
492          // The first attempt almost always fails, don't fill the log with junk
493             LOG_WARN(("PullerConnected(): connect() failed for client %u, "
494                       "try %d, %s",pClient->SN,pClient->x+1,Err2String(Err)));
495          }
496 
497          if(pClient->x++ < 10) {
498          // Wait 20 milliseconds and try again
499             pClient->bReadWait = FALSE;
500             pClient->bWriteWait = FALSE;
501             SetTimeout(pClient,20);
502          }
503          else {
504             pClient->x = 0;
505          }
506       }
507       else {
508          if(pClient->x > 1) {
509          // The second attempt almost always works, don't fill the log with junk
510             LOG_WARN(("PullerConnected(): connect() succeeded for client %u, "
511                       "try %d\n",pClient->SN,pClient->x+1));
512          }
513          pClient->x = 0;
514       }
515    }
516 
517    if(pClient->x == 0)
518    // Only handle sucess/failure if not a retry
519 #endif
520 
521 // BSD returns an error with an error code of EISCONN here.  That agrees
522 // with the man page description of connect().
523 //
524 // Linux does not return an error.  The Linux man page (at least Red Hat's)
525 // doesn't include any details on the expected behavour of connect when used
526 // with non-blocking sockets and select as we are doing here.
527 //
528 
529    if(i != 0 && Err != ERROR_CONNECTED) {
530    // Connection failed.
531       OpComplete(pClient,Err);
532    }
533    else {
534       switch(pPuller->Req) {
535          case SERV_REQ_LOGIN:
536          case SERV_REQ_LOGOUT:
537          case SERV_REQ_SHUTDOWN:
538          case SERV_REQ_LOGIN_AND_LIST:
539          // Login into the server
540             Ret = PullerLogin(pClient);
541             break;
542 
543          case SERV_REQ_STATION_LIST:
544          // Refresh server list
545             Ret = GetStationList(pClient);
546             break;
547 
548          case SERV_REQ_CHECK_CALL:
549             Ret = CheckCall(pClient);
550             break;
551 
552          default:
553             LOG_ERROR(("PullerConnected(), unexpected request %d!\n",
554                      pPuller->Req));
555             DeleteClient(pClient);
556             break;
557       }
558    }
559 
560    return Ret;
561 }
562 
PullerLogin(ClientInfo * pClient)563 int PullerLogin(ClientInfo *pClient)
564 {
565    int WriteSize;
566    char c = 'l';
567    time_t timenow = (time_t) TimeNow.tv_sec;
568    struct tm *tm = localtime(&timenow);
569    int Ret = 0;
570    PullerInfo *pPuller = (PullerInfo *) pClient->p;
571    char *Status;
572    char Qth[MAX_QTH_LEN+1];
573 
574 // no timeout on write, we'll get an error if it fails
575    pClient->bTimeWait = FALSE;
576    pClient->BufSize = 256;
577    pClient->Buf = (char *) malloc(pClient->BufSize);
578 
579    if((WRITE(pClient->Socket,&c,1) != 1)) {
580    // This isn't good.  It's the first thing we've sent.
581    // The buffer can't be full yet !
582       LOG_WARN(("PullerLogin(): write() failed for client %u, %s",
583                pClient->SN,Err2String(ERROR_CODE)));
584       OpComplete(pClient,ERROR_CODE);
585    }
586    else {
587       switch(pPuller->Req) {
588          default:
589             LOG_ERROR(("PullerLogin(): Unknown server request\n"));
590 
591          case SERV_REQ_LOGIN:
592          case SERV_REQ_LOGIN_AND_LIST:
593             Status = PullerStatusBusy ? "BUSY" : "ONLINE";
594             break;
595 
596          case SERV_REQ_SHUTDOWN:
597          case SERV_REQ_LOGOUT:
598             Status = "OFF-V";
599             break;
600       }
601 
602       Qth[0] = 0;
603       if(ShowStatusInInfo) {
604          UpdateConnectedStatus();
605          if(ConnectedStatus[0] != 0) {
606          // We have connect status to display
607             snprintf(Qth,sizeof(Qth)-1,"%s",ConnectedStatus);
608          }
609       }
610 
611       if(Qth[0] == 0) {
612       // Qth not set yet
613          snprintf(Qth,sizeof(Qth)-1,"%s",
614                   PrivateConference ? "Private conference" : ConferenceQth);
615          Qth[MAX_QTH_LEN] = 0;
616 
617          if(UserCountInLocation) {
618             char *UserCount = GetUserCountString();
619             if(strlen(Qth) + strlen(UserCount) <= MAX_QTH_LEN) {
620                strcat(Qth,UserCount);
621             }
622             else {
623                strcpy(&Qth[MAX_QTH_LEN - strlen(UserCount)],UserCount);
624             }
625          }
626       }
627       Qth[MAX_QTH_LEN] = 0;
628 
629       WriteSize = snprintf(pClient->Buf,pClient->BufSize,
630                            "%s%c%c%s\r%s" VERSION "%c(%d:%2d)\r%s\r%s\r",
631                            ConferenceCall,0xac,iLinkDirServer ? 0x3d : 0xac,
632                            ConferencePass,Status,
633                            bEchoIrlpMode ? 'I' : 'B',
634                            tm->tm_hour,tm->tm_mday,Qth,
635                            EmailAdr == NULL ? "" : EmailAdr);
636 
637       if(WriteSize == -1 || WriteSize >= pClient->BufSize) {
638          LOG_ERROR(("PullerLogin(): Temp buffer too small !\n"));
639          ServerStats[pPuller->Server].Failure++;
640          DeleteClient(pClient);
641       }
642       else {
643          pClient->BufSize = WriteSize;
644          pClient->Count = 0;
645          pClient->State = SendBuffer;
646          pClient->NextState = PullerLoginAck;
647          Ret = 1;
648       }
649    }
650 
651    return Ret;
652 }
653 
PullerLoginAck(ClientInfo * pClient)654 int PullerLoginAck(ClientInfo *pClient)
655 {
656    int  BytesRead;
657    char Data;
658    PullerInfo *pPuller = (PullerInfo *) pClient->p;
659    int Server = pPuller->Server;
660 
661    if(pClient->BufSize > 2) {
662    // We've sent our info, now wait for the response from the server
663       pClient->BufSize = 2;
664       pClient->Count = 0;
665    }
666    else if(pClient->bTimeOut) {
667       LOG_WARN(("PullerLoginAck(), server timeout\n"));
668       OpComplete(pClient,ERR_SERVER_TIMEOUT);
669       pClient->Count = 3;
670    }
671 
672    while(pClient->Count < 2) {
673       if((BytesRead = READ(pClient->Socket,&Data,1)) == 1) {
674             pClient->Buf[pClient->Count++] = Data;
675       }
676       else if(BytesRead == 0) {
677       // Connection has closed
678          LOG_WARN(("PullerLoginAck(), server closed connection.\n"));
679          OpComplete(pClient,ERR_CONNECTION_CLOSED);
680          break;
681       }
682       else if(ERROR_CODE == ERROR_WOULD_BLOCK) {
683       // Nothing available to read now, keep waiting
684          SET_WAIT4_RD(pClient);
685       // Wait for up to SERVER_REQ_TO millseconds for the response
686          SetTimeout(pClient,SERVER_REQ_TO);
687          break;
688       }
689       else {
690       // Some kind of error other than no data available
691          LOG_WARN(("PullerLoginAck(), READ() failed, %s",
692                    Err2String(ERROR_CODE)));
693          OpComplete(pClient,ERROR_CODE);
694          break;
695       }
696    }
697 
698    if(pClient->Count == 2) {
699    // If we succeeded in loging in our buffer should contain "ok"
700       pClient->Buf[2] = 0;
701       if(strcmp(pClient->Buf,"OK") == 0) {
702          DPRINTF(("PullerLoginAck(): Client %u successfully updated status.\n",
703                   pClient->SN));
704 
705          if(ConfigPass == 3) {
706          // Do another yet another pass on the configuration file now that
707          // we've downloaded a station list and have logged in twice.
708          // This gives time for our IP address to propagate between directory
709          // servers to ensure that connect commands embedded in the
710          // configuration file are not refused because our IP address isn't
711          // listed in the directory server yet...
712             DoConfigPass(4);
713          }
714 
715          switch(pPuller->Req) {
716             case SERV_REQ_LOGIN:
717             // We're done
718                OpComplete(pClient,0);
719                break;
720 
721             case SERV_REQ_LOGIN_AND_LIST:
722             // Now list the stations
723                ServerStats[Server].Success++;
724                DeleteClient(pClient);
725                break;
726 
727             case SERV_REQ_SHUTDOWN:
728                LOG_NORM(("Logged out, exiting.\n"));
729                DeleteClient(pClient);
730                bRunning = FALSE;
731                break;
732 
733             default:
734                LOG_ERROR(("PullerLoginAck(), unexpected request %d!\n",
735                         pPuller->Req));
736                DeleteClient(pClient);
737                break;
738          }
739       }
740       else if(strcmp(pClient->Buf,"NO") == 0) {
741          LOG_WARN(("PullerLoginAck(): login server rejected login attempt for "
742                   "client %u, exiting.\n",pClient->SN));
743          bRunning = FALSE;
744          OpComplete(pClient,ERR_SERVER_ERR);
745       }
746       else {
747          LOG_WARN(("PullerLoginAck(): login error server returned \"%s\" for"
748                   "client %u.\n",pClient->Buf,pClient->SN));
749          OpComplete(pClient,ERR_SERVER_ERR);
750       }
751    }
752 
753    return 0;
754 }
755 
GetStationList(ClientInfo * pClient)756 int GetStationList(ClientInfo *pClient)
757 {
758    char Request[12];
759    int WriteLen;
760    PullerInfo *pPuller = (PullerInfo *) pClient->p;
761 
762    pPuller->Req = SERV_REQ_STATION_LIST;
763    pClient->BufSize = 256;
764    pClient->Buf = (char *) malloc(pClient->BufSize);
765    pClient->State = ReadStationData;
766    SET_WAIT4_RD(pClient);
767 
768 // Wait for up to SERVER_REQ_TO millseconds for data
769    SetTimeout(pClient,SERVER_REQ_TO);
770 
771    if(DirCompression) {
772    // Ask for a compressed, differential station list
773       snprintf(Request,sizeof(Request),"F%s\r",SnapShortID);
774    }
775    else if(iLinkDirServer) {
776       strcpy(Request,"S");
777    }
778    else {
779       strcpy(Request,"s");
780    }
781 
782    WriteLen = strlen(Request);
783 
784    if((WRITE(pClient->Socket,Request,WriteLen) != WriteLen)) {
785    // This isn't good.  It's the first thing we've sent.
786    // The buffer can't be full yet !
787       LOG_WARN(("GetStationList(): write() failed, %s",Err2String(ERROR_CODE)));
788       DeleteClient(pClient);
789    }
790 
791    return 0;
792 }
793 
794 // Read station list from the directory server
ReadStationData(ClientInfo * p)795 int ReadStationData(ClientInfo *p)
796 {
797    char *cp;
798    PullerInfo *pPull = (PullerInfo *) p->p;
799    int Err;
800 
801    while(p->Err == 0 && !pPull->bAbort) {
802       if(p->bTimeOut) {
803          p->Err = ERR_SERVER_TIMEOUT;
804          LOG_ERROR(("ReadStationData(): server time out client %u.\n",p->SN));
805          break;
806       }
807 
808       if(!pPull->bPreambleRead) {
809       // looking for first 4 bytes to determine if stream is compressed or not
810          if(p->Count < 4) {
811             if(!GetRxData(p)) {
812                break;
813             }
814          }
815          else {
816             ParseStationList(p,&p->Buf[p->RetCount],4);
817          }
818       }
819       else if(pPull->bCompressed) {
820          if(pPull->ZRetCount > 0 && pPull->ZCount > 0) {
821          // move remaining data in buffer to start
822             memmove(pPull->ZBuf,&pPull->ZBuf[pPull->ZRetCount],pPull->ZCount+1);
823          }
824          pPull->ZRetCount = 0;
825          pPull->c_stream.next_out = (Bytef *) &pPull->ZBuf[pPull->ZCount];
826          pPull->c_stream.avail_out = pPull->ZBufSize - pPull->ZCount - 1;
827 
828          if(!pPull->bCompressedEOF) {
829             if(pPull->c_stream.avail_in == 0) {
830             // need more data to decompress
831                if(p->Count == 0 && !GetRxData(p)) {
832                   break;
833                }
834                pPull->c_stream.next_in = (Bytef *) &p->Buf[p->RetCount];
835                pPull->c_stream.avail_in = p->Count;
836                p->Count = 0;
837                p->RetCount = 0;
838             }
839 
840             if(pPull->c_stream.avail_in > 0) {
841                if(pPull->c_stream.avail_out == 0) {
842                   LOG_ERROR(("ReadStationData(): Zbuf overflow for client "
843                              "%u.\n",p->SN));
844                   p->Err = ERR_DECOMPRESS_ERR;
845                   break;
846                }
847 
848                if((Err = inflate(&pPull->c_stream,Z_NO_FLUSH)) != Z_OK) {
849                   if(Err == Z_STREAM_END) {
850                      pPull->bCompressedEOF = TRUE;
851                   }
852                   else {
853                      LOG_ERROR(("ReadStationData(): inflate returned 0x%x for "
854                                 "client %u.\n",Err,p->SN));
855                      p->Err = ERR_DECOMPRESS_ERR;
856                      break;
857                   }
858                }
859                pPull->ZCount = (char *) pPull->c_stream.next_out - pPull->ZBuf;
860                pPull->ZBuf[pPull->ZCount] = 0;
861             }
862          }
863          else if(pPull->ZCount > 0) {
864          // Consume the remaining data
865             p->Err = ERR_CONNECTION_CLOSED;
866             ParseStationList(p,&pPull->ZBuf[pPull->ZRetCount],pPull->ZCount);
867             break;
868          }
869          else {
870             LOG_ERROR(("ReadStationData(): bCompressedEOF detected before end "
871                        "of station data detected for client %u.\n",p->SN));
872             p->Err = ERR_DECOMPRESS_ERR;
873             break;
874          }
875 
876          while(pPull->ZCount > 0 &&
877             (cp = strchr((char *) &pPull->ZBuf[pPull->ZRetCount],'\n')) != NULL)
878          {  // We have a complete line
879             int   Count;
880 
881             Count = (cp - &pPull->ZBuf[pPull->ZRetCount]) + 1;
882             *cp = 0; // remove the line feed
883             ParseStationList(p,&pPull->ZBuf[pPull->ZRetCount],Count-1);
884             pPull->ZCount -= Count;
885             pPull->ZRetCount += Count;
886             if(pPull->bAbort) {
887                break;
888             }
889          }
890       }
891       else {   // Not compressed
892          while(p->Count > 0 && (cp = strchr(&p->Buf[p->RetCount],'\n')) != NULL)
893          {  // We have a complete line
894             int   Count;
895 
896             Count = (cp - &p->Buf[p->RetCount]) + 1;
897             *cp = 0; // remove the line feed
898             ParseStationList(p,&p->Buf[p->RetCount],Count-1);
899             p->Count -= Count;
900             p->RetCount += Count;
901             if(pPull->bAbort) {
902                break;
903             }
904          }
905 
906          if(pPull->bAbort || !GetRxData(p)) {
907             break;
908          }
909 
910          if(p->Err != 0 && p->Count > 0) {
911          // Consume the remaining data
912             ParseStationList(p,&p->Buf[p->RetCount],p->Count);
913          }
914       }
915    }
916 
917    if(p->Err != 0 || pPull->bAbort) {
918       if(pPull->bComplete) {
919          if(pPull->bDifferential) {
920             DPRINTF(("Differental station list completed, %d added, %d deleted,"
921                      " %d updated.\n",pPull->NewStations,
922                      pPull->DeletedStations,pPull->UpDatedStations));
923          }
924          else {
925             DPRINTF(("Full station list downloaded successfully, "
926                      "%d stations listed.\n",pPull->NewStations));
927          }
928          OpComplete(p,0);
929          if(ConfigPass == 2) {
930          // Do another pass on the configuration file now that we've downloaded
931          // a valid station list
932             DoConfigPass(3);
933          }
934       }
935       else {
936          OpComplete(p,p->Err);
937       }
938    }
939 
940    return 0;
941 }
942 
943 // Parse a line of text from the directory server.
944 // Return true when complete
ParseStationList(ClientInfo * pClient,char * Buf,int Count)945 void ParseStationList(ClientInfo *pClient,char *Buf,int Count)
946 {
947    PullerInfo *pPuller = (PullerInfo *) pClient->p;
948    UserInfo *pUser;
949    UserInfo UserLookup;
950    char *cp;
951    int i;
952    int DataLen = 0;
953    int bNewUser = FALSE;
954    int bNewIPAdr = FALSE;
955    int State = pClient->x;
956    int Err;
957    int bEmptyField = FALSE;
958 
959    if(pPuller->bDifferential && Count == 1 && *Buf == '.') {
960       bEmptyField = TRUE;
961    }
962 
963    if(Count == sizeof(EndOfData)-1 &&
964       strncmp(Buf,EndOfData,sizeof(EndOfData)-1) == 0)
965    {  // End of data marker
966       if(pPuller->bDifferential) {
967          if(pPuller->bStationListEndFound) {
968             pPuller->bComplete = TRUE;
969             pPuller->bAbort = TRUE;
970             CleanUserList(TRUE);
971          }
972          pPuller->bStationListEndFound = TRUE;
973          State = 6;
974          if(pPuller->StationCount != 0) {
975             LOG_ERROR(("ParseStationList(): Warning: deleted station marker"
976                        "found @ StationCount %d.\n",pPuller->StationCount));
977          }
978       }
979       else {
980          CleanUserList(FALSE);
981          pPuller->bComplete = TRUE;
982          pPuller->bAbort = TRUE;
983       }
984    }
985    else if(pPuller->bIgnore) {
986    // ignoring a station entry
987       if(State++ == 5) {
988          pPuller->bIgnore = FALSE;
989       // Back to expecting a callsign
990          State = 2;
991       }
992    }
993    else switch(State) {
994       case 0:  // First 4 bytes of data are available
995          if(strncmp(Buf,StartOfData,sizeof(StartOfData)-1) == 0) {
996          // Uncompressed station list
997             State++;
998             DataLen = sizeof(StartOfData);
999          }
1000          else if(strncmp(Buf,DiffData,sizeof(DiffData)-1) == 0) {
1001          // Differential station list
1002             State++;
1003             pPuller->bDifferential= TRUE;
1004             DataLen = sizeof(DiffData)-1;
1005          }
1006          else if(!pPuller->bCompressed) {
1007          // not start of data mark, check to see if it's a length
1008             DataLen = 0;
1009             for(i = 3; i >= 0; i--) {
1010                DataLen <<= 8;
1011                DataLen += (unsigned char) Buf[i];
1012             }
1013 
1014             // Assume the station list will be < 1 megabyte
1015             if(DataLen < MAX_STATION_LIST_SIZE) {
1016             // Assume it's a compressed station list
1017                pPuller->bCompressed = TRUE;
1018                DataLen = 4;
1019 
1020                if((Err = inflateInit(&pPuller->c_stream)) != Z_OK) {
1021                   LOG_ERROR(("ParseStationList(): inflateInit returned 0x%x\n",
1022                              Err));
1023                   pClient->Err = ERR_DECOMPRESS_ERR;
1024                   pPuller->bAbort = TRUE;
1025                }
1026                else {
1027                   pPuller->ZBufSize = pClient->BufSize * 4;
1028                   pPuller->ZBuf = malloc(pPuller->ZBufSize);
1029                   if(pPuller->ZBuf == NULL) {
1030                      LOG_ERROR(("ParseStationList(): malloc failed.\n"));
1031                      pClient->Err = ERR_MALLOC;
1032                      pPuller->bAbort = TRUE;
1033                   }
1034                }
1035             }
1036             else {
1037                pPuller->bAbort = TRUE;
1038                LOG_ERROR(("ParseStationList(): Expected StartOfData got "
1039                           "\"%s\".\n",Buf));
1040             }
1041          }
1042          else {
1043             pPuller->bAbort = TRUE;
1044             LOG_ERROR(("ParseStationList(): Expected StartOfData got "
1045                        "\"%s\".\n",Buf));
1046          }
1047 
1048          if(!pPuller->bPreambleRead) {
1049          // Consume Preamble
1050             pPuller->bPreambleRead = TRUE;
1051             pClient->RetCount += DataLen;
1052             pClient->Count -= DataLen;
1053          }
1054          break;
1055 
1056       case 1:  // parse record count and snapshot ID
1057          sscanf(Buf,"%d",&pPuller->StationCount);
1058          if((cp = strchr(Buf,':')) != NULL) {
1059          // We have a snapshot ID, save it.
1060             snprintf(SnapShortID,sizeof(SnapShortID),"%s",cp+1);
1061          }
1062          State++;
1063          break;
1064 
1065       case 2:  // Callsign
1066          State++;
1067          if(Count > MAX_CALL_LEN) {
1068             LOG_ERROR(("ParseStationList(): Callsign \"%s\" is too long.\n",
1069                      Buf));
1070             pPuller->bIgnore = TRUE;
1071          }
1072          else {
1073             strcpy(pPuller->Callsign,Buf);
1074          }
1075          break;
1076 
1077       case 3:  // Qth / status
1078          State++;
1079          if(bEmptyField) {
1080             pPuller->Qth[0] = 0;
1081             break;
1082          }
1083 
1084          if((cp = strrchr(Buf,'[')) != NULL) {
1085             *cp++ = 0;
1086             i = sscanf(cp,"%*s %d:%d",&pPuller->LocalHr,&pPuller->LocalMin);
1087             if(i != 2) {
1088                pPuller->LocalHr = -1;
1089             }
1090 
1091             if(strncmp(cp,"BUSY",4) == 0) {
1092                pPuller->bBusy = TRUE;
1093             }
1094             else if(strncmp(cp,"ON",2) == 0) {
1095                pPuller->bBusy = FALSE;
1096             }
1097             else {
1098             // Invalid entry, unknown state
1099                pPuller->bIgnore = TRUE;
1100             }
1101          }
1102          else {
1103          // Invalid entry, no status
1104             pPuller->bIgnore = TRUE;
1105          }
1106 
1107          if(pPuller->bIgnore) {
1108             int x = pPuller->NewStations + pPuller->DeletedStations +
1109                     pPuller->UpDatedStations;
1110             RightTrim(pPuller->Callsign);
1111             RightTrim(Buf);
1112             if(pPuller->Callsign[0] == 0 && (DebugLevel > 0 || x == 0)) {
1113             // Message from EchoLink Directory server, log it
1114                LOG_ERROR(("Msg from EchoLink: %s\n",Buf));
1115             }
1116             break;
1117          }
1118 
1119          if(strlen(Buf) > MAX_QTH_LEN) {
1120             LOG_ERROR(("ParseStationList(): Qth \"%s\" is too long.\n",
1121                      Buf));
1122             pPuller->bIgnore = TRUE;
1123          }
1124          else {
1125             strcpy(pPuller->Qth,Buf);
1126          }
1127          break;
1128 
1129       case 4:  // NodeID
1130          State++;
1131          if(bEmptyField) {
1132             pPuller->NodeID = 0;
1133          }
1134          else if(sscanf(Buf,"%d",&pPuller->NodeID) != 1) {
1135             LOG_ERROR(("ParseStationList(): can't convert NodeID \"%s\".\n",
1136                      Buf));
1137             pPuller->bIgnore = TRUE;
1138          }
1139          break;
1140 
1141       case 5:  // IP address
1142          UserLookup.Callsign = pPuller->Callsign;
1143          if((pUser = avl_find(UserTree,&UserLookup)) == NULL) {
1144          // New user
1145             D2PRINTF(("ParseStationList(): Adding new user \"%s\".\n",
1146                      pPuller->Callsign));
1147             pUser = CreateNewUser(pPuller->Callsign);
1148             bNewUser = TRUE;
1149             bNewIPAdr = TRUE;
1150             pPuller->NewStations++;
1151          }
1152          else {
1153             pPuller->UpDatedStations++;
1154             D2PRINTF(("ParseStationList(): Updating user \"%s\".\n",
1155                      pPuller->Callsign));
1156          }
1157 
1158          if(pUser != NULL) {
1159             pUser->bActive = pUser->bRefreshed = TRUE;
1160             pUser->bAuthorized = TRUE;
1161             pUser->LastHeard = TimeNow.tv_sec;
1162 
1163             if(pPuller->Qth[0] != 0) {
1164                pUser->bBusy = pPuller->bBusy;
1165                if(pUser->Qth != NULL) {
1166                   free(pUser->Qth);
1167                }
1168                pUser->Qth = strdup(pPuller->Qth);
1169             }
1170 
1171             if(!bEmptyField) {
1172                if(!bNewUser && pUser->HisAdr.ADDR != inet_addr(Buf)) {
1173                // The user's IP address has changed, remove the old reference
1174                // from the IP address tree
1175                   avl_delete(UserIPAdrTree,pUser);
1176                   bNewIPAdr = TRUE;
1177                }
1178                if(iLinkDirServer) {
1179                   DeScrambleIP(Buf);
1180                }
1181 
1182                pUser->HisAdr.ADDR = inet_addr(Buf);
1183 
1184                if(pUser->HisAdr.ADDR == INADDR_NONE) {
1185                   LOG_ERROR(("Invalid IP address \"%s\" for %s\n",Buf,
1186                              pUser->Callsign));
1187                }
1188             }
1189 
1190             pUser->NodeID = pPuller->NodeID;
1191 
1192             if(bNewUser) {
1193                avl_insert(UserTree,pUser);
1194                pPuller->NewStations++;
1195             }
1196 
1197             if(bNewIPAdr) {
1198                avl_insert(UserIPAdrTree,pUser);
1199             }
1200          }
1201          if(--pPuller->StationCount == 0 && pPuller->bDifferential) {
1202          // done with the active stations next are the deleted stations
1203             State = 6;
1204          }
1205          else {
1206             State = 2;  // back to looking for callsign
1207          }
1208          break;
1209 
1210       case 6:  // Deleted stations
1211          UserLookup.Callsign = Buf;
1212          if((pUser = avl_find(UserTree,&UserLookup)) == NULL) {
1213          // user not found
1214             LOG_ERROR(("ParseStationList(): Deleted user \"%s\" not found.\n",
1215                      Buf));
1216          }
1217          else {
1218             pUser->bActive = FALSE;
1219             pPuller->DeletedStations++;
1220          }
1221          break;
1222    }
1223 
1224    pClient->x = State;
1225 }
1226 
1227 // Remove any stations from the user list that are no longer present
1228 // in the list we just pulled from the directory server
CleanUserList(int bDifferential)1229 void CleanUserList(int bDifferential)
1230 {
1231    UserInfo *pUser;
1232    UserInfo *pNextUser;
1233    struct avl_traverser avl_trans;
1234    FILE  *fp = NULL;
1235    int   bDeleteUser;
1236 
1237    ActiveDirEntries = 0;
1238    InactiveDirEntries = 0;
1239 
1240    if(WriteHostFile) {
1241       if((fp = fopen(ECHOLINK_HOSTS_TMP_FILE,"w")) != NULL) {
1242          fprintf(fp,"#IP Adr\tCallsign\tNode number\tQth\n");
1243       }
1244    }
1245 
1246    pNextUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
1247    while((pUser = pNextUser) != NULL) {
1248    // NB: Get the next user now in case we deleted the current user
1249       pNextUser = (UserInfo *) avl_t_next(&avl_trans);
1250       bDeleteUser = FALSE;
1251 
1252       if(strcmp(pUser->Callsign,ConferenceCall) == 0) {
1253       // It's us !
1254          OurNodeID = ntohl(pUser->NodeID);
1255       }
1256 
1257       if(bDifferential) {
1258          if(!pUser->bActive) {
1259          // He's gone
1260             if((TimeNow.tv_sec - pUser->LastHeard) > InactiveDirTimeout) {
1261                bDeleteUser = TRUE;
1262             }
1263          }
1264       }
1265       else {
1266       // Full list
1267          if(!pUser->bRefreshed) {
1268          // He's gone
1269             if((TimeNow.tv_sec - pUser->LastHeard) > InactiveDirTimeout) {
1270                bDeleteUser = TRUE;
1271             }
1272             else {
1273             // Keep him around for a while to minimize the number of time
1274             // we have to ask the directory server about users who have just
1275             // logged in.
1276                pUser->bActive = FALSE;
1277             }
1278          }
1279       }
1280 
1281 
1282       if(bDeleteUser) {
1283          DeleteUser(pUser,FALSE);
1284       }
1285       else {
1286          pUser->bRefreshed = FALSE;
1287          if(pUser->bActive || IncludeAllHosts) {
1288             ActiveDirEntries++;
1289             if(fp != NULL && pUser->bAuthorized) {
1290                if(QthInHostsFile) {
1291                   fprintf(fp,"%s\t%s\t# %d\t%s%s\n",
1292                           inet_ntoa(pUser->HisAdr.i.sin_addr),
1293                           pUser->Callsign,pUser->NodeID,
1294                           pUser->Qth,
1295                           pUser->bActive ? "" : " (off line)");
1296                }
1297                else {
1298                   fprintf(fp,"%s\t%s\t# %d%s\n",
1299                           inet_ntoa(pUser->HisAdr.i.sin_addr),
1300                           pUser->Callsign,pUser->NodeID,
1301                           pUser->bActive ? "" : " (off line)");
1302                }
1303             }
1304          }
1305          else {
1306             InactiveDirEntries++;
1307          }
1308       }
1309    }
1310 
1311    if(fp != NULL) {
1312       fclose(fp);
1313       unlink(ECHOLINK_HOSTS_FILE);
1314       rename(ECHOLINK_HOSTS_TMP_FILE,ECHOLINK_HOSTS_FILE);
1315       EventHook("hostfile");
1316    }
1317 }
1318 
CheckCallResp(ClientInfo * pClient)1319 int CheckCallResp(ClientInfo *pClient)
1320 {
1321    int  BytesRead;
1322    char Data;
1323    PullerInfo *pPuller = (PullerInfo *) pClient->p;
1324    UserInfo *pUser;
1325    UserInfo UserLookup;
1326    char *CheckArg = (char *) pPuller->Arg;
1327    char *cp;
1328    int Err = 0;
1329    char temp[32];
1330 
1331    if(pClient->bReadReady) {
1332       if((BytesRead = READ(pClient->Socket,&Data,1)) == 1) {
1333       // we have our answer
1334          cp = strchr(CheckArg,'\r');
1335          *cp++ = 0;
1336          UserLookup.HisAdr.ADDR = inet_addr(cp);
1337          UserLookup.Callsign = CheckArg;
1338          if(Data == '1') {
1339             if((pUser = avl_find(UserTree,&UserLookup)) == NULL) {
1340             // User doesn't exists, create him
1341                pUser = CreateNewUser(CheckArg);
1342                pUser->HisAdr.ADDR = UserLookup.HisAdr.ADDR;
1343                pUser->bActive = TRUE;
1344                pUser->bAuthorized = TRUE;
1345                avl_insert(UserTree,pUser);
1346                avl_insert(UserIPAdrTree,pUser);
1347                LOG_NORM(("CheckCallResp(): added authorized client %s.\n",
1348                          pUser->Callsign));
1349             }
1350             else {
1351             // Existing user
1352                if(pUser->HisAdr.ADDR != UserLookup.HisAdr.ADDR) {
1353                   strcpy(temp,inet_ntoa(pUser->HisAdr.i.sin_addr));
1354 
1355                   LOG_NORM(("%s's IP address changed from %s to %s.\n",
1356                             pUser->Callsign,temp,
1357                             inet_ntoa(UserLookup.HisAdr.i.sin_addr)));
1358                   pUser->HisAdr.ADDR = UserLookup.HisAdr.ADDR;
1359                   pUser->bAuthorized = TRUE;
1360                }
1361             }
1362          }
1363          else {
1364             LOG_NORM(("CheckCallResp(): server returned 0x%x for client %s.\n",
1365                       Data,CheckArg));
1366          }
1367       }
1368       else {
1369          Err = errno;
1370       }
1371    }
1372    else if(pClient->bTimeOut) {
1373       Err = ERR_SERVER_TIMEOUT;
1374       LOG_NORM(("CheckCallResp(): Timed out waiting for a response.\n"));
1375    }
1376 
1377    OpComplete(pClient,Err);
1378 
1379    return 0;
1380 }
1381 
CheckCall(ClientInfo * pClient)1382 int CheckCall(ClientInfo *pClient)
1383 {
1384    char c = 'v';
1385    int ReqLen;
1386    char *cp;
1387 
1388    PullerInfo *pPuller = (PullerInfo *) pClient->p;
1389    cp = (char *) pPuller->Arg;
1390 
1391    ReqLen = strlen(cp);
1392    if(WRITE(pClient->Socket,&c,1) != 1 ||
1393       WRITE(pClient->Socket,cp,ReqLen) != ReqLen)
1394    {
1395    // This isn't good.  It's the first thing we've sent.
1396    // The buffer can't be full yet !
1397       LOG_WARN(("CheckCall(): write() failed, %s",Err2String(ERROR_CODE)));
1398       DeleteClient(pClient);
1399    }
1400 
1401    pClient->State = CheckCallResp;
1402    SET_WAIT4_RD(pClient);
1403 
1404 // Wait for up to SERVER_REQ_TO milliseconds for the response
1405    SetTimeout(pClient,SERVER_REQ_TO);
1406 
1407    return 0;
1408 }
1409 
PullerCleanup(ClientInfo * p)1410 void PullerCleanup(ClientInfo *p)
1411 {
1412    PullerInfo *pPuller = (PullerInfo *) p->p;
1413 
1414    if(pPuller->Req == SERV_REQ_LOGIN_AND_LIST && p->Err == 0) {
1415    // Start second half of request
1416       ServerRequest(SERV_REQ_STATION_LIST,pPuller->Server,NULL);
1417    }
1418 
1419    if(pPuller->Arg != NULL) {
1420       free(pPuller->Arg);
1421    }
1422 
1423    if(pPuller->ZBuf != NULL) {
1424       free(pPuller->ZBuf);
1425    }
1426 
1427    if(pPuller->ZBufSize > 0) {
1428       inflateEnd(&pPuller->c_stream);
1429    }
1430 
1431    free(p->p);
1432    p->p = NULL;
1433 }
1434 
ServerRequestComplete(ClientInfo * p)1435 int ServerRequestComplete(ClientInfo *p)
1436 {
1437    PullerInfo *pPuller = (PullerInfo *) p->p;
1438    SERVER_REQUEST Request = pPuller->Req;
1439    void *Arg = pPuller->Arg;
1440    int Server = pPuller->Server;
1441    int Err = p->Err;
1442 
1443    pPuller->Arg = NULL;
1444    DeleteClient(p);
1445 
1446    if(Err == 0) {
1447       ServerStats[Server].Success++;
1448    }
1449    else {
1450    // Requested operation failed
1451       ServerStats[Server].Failure++;
1452       LOG_ERROR(("ServerRequest %s failed with server %s, Err %d\n",
1453                  Req2String[Request],DirServerHost[Server],Err));
1454 
1455       if(++Server < NUM_DIRECTORY_SERVERS && DirServerHost[Server] != NULL) {
1456       // Another directory server to try
1457          LOG_WARN(("Trying %s with backup server %s\n",
1458                    Req2String[Request],DirServerHost[Server]));
1459          ServerRequest(Request,Server,Arg);
1460          Arg = NULL;
1461       }
1462       else if(Request == SERV_REQ_SHUTDOWN) {
1463       // Oh well, we tried.
1464          LOG_ERROR(("Couldn't Log out, exiting anyway.\n"));
1465          bRunning = FALSE;
1466       }
1467    }
1468 
1469    if(Arg != NULL) {
1470       free(Arg);
1471    }
1472    return 0;
1473 }
1474 
1475 
DeScrambleIP(char * IO)1476 void DeScrambleIP(char *IO)
1477 {
1478    int InputLen;
1479    int i;
1480    int KeyOffset = 0;
1481    char Key[] = "91182092262753893";
1482    char Substute1[] = "4325198076";
1483    char Substute2[] = "3719458602";
1484    char *InputSave = strdup(IO);
1485 
1486    InputLen = strlen(IO);
1487 
1488    D3PRINTF(("DeScrambleIP(): Input string \"%s\".\n",IO));
1489 
1490    for(i = 0; i < InputLen-2; i++) {
1491       IO[i] = IO[i] - Key[KeyOffset++];
1492       if(KeyOffset > 14) {
1493          KeyOffset = 0;
1494       }
1495    }
1496 
1497    for(i = 0; i < InputLen; i++) {
1498       if(isdigit(IO[i])) {
1499          IO[i] = Substute1[IO[i] - '0'];
1500       }
1501       else if(IO[i] == '}') {
1502          IO[i] = '.';
1503       }
1504    }
1505 
1506    for(i = 0; i < InputLen; i++) {
1507       if(isdigit(IO[i])) {
1508          IO[i] = Substute2[IO[i] - '0'];
1509       }
1510       else if(IO[i] == '=') {
1511          IO[i] = '.';
1512       }
1513    }
1514 
1515    if(inet_addr(IO) == INADDR_NONE) {
1516       LOG_ERROR(("DeScrambleIP() failed: in \"%s\", out \"%s\"\n",InputSave,IO));
1517    }
1518    free(InputSave);
1519 
1520    D3PRINTF(("DeScrambleIP(): Output string \"%s\".\n",IO));
1521 }
1522 
RightTrim(char * Buf)1523 void RightTrim(char *Buf)
1524 {
1525    char *cp;
1526    int Strlen = strlen(Buf);
1527 
1528    if(Strlen > 0) {
1529       cp = Buf + Strlen - 1;
1530       for( ; ; ) {
1531          if(*cp == ' ') {
1532             *cp = 0;
1533          }
1534          else {
1535             break;
1536          }
1537 
1538          if(cp-- == Buf) {
1539             break;
1540          }
1541       }
1542    }
1543 }
1544 
DirCleanup()1545 void DirCleanup()
1546 {
1547    UserInfo *pUser;
1548    UserInfo *pNextUser;
1549    struct avl_traverser avl_trans;
1550 
1551    pNextUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
1552    while((pUser = pNextUser) != NULL) {
1553       pNextUser = (UserInfo *) avl_t_next(&avl_trans);
1554 
1555       DeleteUser(pUser,FALSE);
1556    }
1557 }
1558 
1559