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