1 /*
2 gnauralnet_main.c
3 Central code to achieve cooperative clock sync'ing between peers on a network
4 Depends on:
5 gnauralnet.h
6 gnauralnet_clocksync.c
7 gnauralnet_lists.c
8 gnauralnet_main.c
9 gnauralnet_socket.c
10
11 Copyright (C) 2009 Bret Logan
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28 /////////////////////////////////////////////
29 //User creates these two:
30 //extern int GN_ProcessIncomingData ();
31 //extern int GN_MainLoop ();
32
33 ////////////////////////////////////////
34 /*
35 GN_start([user function]) and GN_stop() are all user must run, then
36 for user function set up a Main Loop like this:
37
38 while (GNAURALNET_RUNNING == GN_My.State)
39 {
40 GN_Socket_RecvMessage ([time to wait for data]);
41 GN_ProcessOutgoingData (all the friends);//optional
42 }
43 GN_My.State = GNAURALNET_STOPPED;
44 return GNAURALNET_SUCCESS;
45 }
46
47 to wait for recv's from GN_Socket_RecvMessage(), handling incoming/outgoing/
48 invite/timing data with GN_Process**() functions
49 NOTE: if GNAURALNET_GTHREADS is set, GN_start() starts a thread
50 that stays in an internal loop calling GN_recv*Buffer(). If not set,
51 user needs to call GN_recv*Buffer() in their own loop like
52 while "(0 == GN_My.State) {}" until GN_stopped breaks it.
53
54 To compile with the MinGW gcc cross compiler, be sure you have gnauralnet.h:
55 i586-mingw32msvc-g++ -g -O2 -o gnauralnet.exe -mms-bitfields -mwindows \
56 -I/usr/i586-mingw32msvc/include/glib-2.0 \
57 -I/usr/i586-mingw32msvc/lib/glib-2.0/include \
58 -DGNAURAL_WIN32 gnauralnet_main.c gnauralnet_clocksync.c gnauralnet_socket.c \
59 gnauralnet_lists.c -lwsock32
60
61 You should strip it too:
62 i586-mingw32msvc-strip gnauralnet.exe
63 */
64 //VERSION INFO:
65 //This is the first
66
67 #include "gnauralnet.h"
68
69 //Global variables:
70 //this is the struct holding all major single-instance kinds of globals:
71 GN_Globals GN_My = { GNAURALNET_STOPPED };
72
73 int GN_DebugFlag = 0;
74 GN_CommBuffer GN_RecvBuffer;
75 GN_CommBuffer GN_SendBuffer;
76 unsigned short GN_ScheduleFingerprint = 0;
77 //If user doesn't set these next two manually in GN_start (), they
78 //default to internals functions:
79 int (*GN_MainLoop) (void *ptr) = NULL; //defaults to GN_MainLoop_default
80 void (*GN_ProcessIncomingData) (char *MsgBuf, int sizeof_MsgBuf, struct sockaddr_in * saddr_remote) = NULL; //defaults to GN_ProcessIncomingData_default
81
82 ///////////////////////////////////////////////////////////
83 //pass NULL to this and it just uses the built-in Main Loop, GN_MainLoop_default ()
GN_start(int (* main_loop_func)(void * ptr),void (* incoming_data_func)(char * MsgBuf,int sizeof_MsgBuf,struct sockaddr_in * saddr_remote))84 int GN_start (int (*main_loop_func) (void *ptr),
85 void (*incoming_data_func) (char *MsgBuf, int sizeof_MsgBuf,
86 struct sockaddr_in * saddr_remote))
87 {
88 if (NULL == main_loop_func)
89 {
90 GN_MainLoop = GN_MainLoop_default;
91 }
92 else
93 {
94 GN_MainLoop = main_loop_func;
95 }
96
97 if (NULL == incoming_data_func)
98 {
99 GN_ProcessIncomingData = GN_ProcessIncomingData_default;
100 }
101 else
102 {
103 GN_ProcessIncomingData = incoming_data_func;
104 }
105
106 GN_DBGOUT_INT ("Size of GN_CommBuffer:", sizeof (GN_CommBuffer));
107 //init network:
108 if (GN_init () != 0)
109 {
110 perror ("Init Network Failed\n");
111 GN_My.State = GNAURALNET_STOPPED;
112 return GNAURALNET_FAILURE;
113 }
114 GN_My.State = GNAURALNET_RUNNING;
115 #ifdef GNAURALNET_GTHREADS
116 GThread *GN_serverthread = NULL;
117 GError *thread_err = NULL;
118
119 if (NULL ==
120 (GN_serverthread =
121 g_thread_create ((GThreadFunc) GN_MainLoop, (void *) NULL, FALSE,
122 &thread_err)))
123 {
124 GN_ERROUT ("g_thread_create failed:");
125 GN_ERROUT (thread_err->message);
126 g_error_free (thread_err);
127 return GNAURALNET_FAILURE;
128 }
129 else
130 {
131 GN_DBGOUT ("Gnauralnet server thread created successfully");
132 return GNAURALNET_SUCCESS;
133 }
134 #endif
135 //at this point, users needs to set up their own send/recv loop, akin to
136 //the threaded GN_MainLoop ((void *) NULL) example.
137 return GNAURALNET_SUCCESS;
138 }
139
140 /////////////////////////////////////////////////////////////
141 //This is the default internal one; user can create their own
142 //by setting
GN_MainLoop_default(void * arg)143 int GN_MainLoop_default (void *arg)
144 {
145 GN_DBGOUT ("Entering GnauralNet MainLoop ()...");
146 while (GNAURALNET_RUNNING == GN_My.State)
147 {
148 //Receive and process packets for GN_MainLoopInterval_usecs time:
149 GN_Socket_RecvMessage (GN_My.Socket, (char *) &GN_RecvBuffer,
150 sizeof (GN_RecvBuffer), GN_MainLoopInterval_secs,
151 GN_MainLoopInterval_usecs);
152
153 //Send message to ONE friend on the FriendList if there are any:
154 //NOTE: the do-loop is just to be sure a friend still gets hit even
155 // if defriending occurs.
156 int RepeatFlag;
157 GN_Friend *curFriend;
158
159 do
160 {
161 RepeatFlag = GNAURALNET_FALSE;
162 curFriend = GN_FriendList_NextFriend ();
163
164 //see if we have no friends:
165 if (NULL == curFriend)
166 {
167 break; //having no friends isn't a failure!
168 }
169
170 //See if Friend has strayed enough to be defriended:
171 if (GN_MISSED_RECV_LIMIT < ++(curFriend->SendRecvTally))
172 {
173 GN_DBGOUT ("Unresponsive, defriending:");
174 GN_PrintAddressInfo (curFriend->ID, curFriend->IP, curFriend->Port);
175 GN_FriendList_DeleteFriend (curFriend);
176 RepeatFlag = GNAURALNET_TRUE;
177 }
178 }
179 while (GNAURALNET_FALSE != RepeatFlag);
180
181 //this does the send too (assuming curFriend != NULL):
182 GN_ProcessOutgoingData (curFriend);
183 }
184 GN_My.State = GNAURALNET_STOPPED;
185 return GNAURALNET_SUCCESS;
186 }
187
188 ///////////////////////////////////////
189 //where incoming data gets processed
GN_ProcessIncomingData_default(char * MsgBuf,int sizeof_MsgBuf,struct sockaddr_in * saddr_remote)190 void GN_ProcessIncomingData_default (char *MsgBuf, int sizeof_MsgBuf,
191 struct sockaddr_in *saddr_remote)
192 {
193 //First check for the kind of data.
194 //The only kind we currently expect is a GN_RecvBuffer:
195 if (MsgBuf != (char *) &GN_RecvBuffer ||
196 sizeof (GN_RecvBuffer) != sizeof_MsgBuf)
197 {
198 GN_ERROUT ("recv'd buffer not correct type");
199 return;
200 }
201
202 //to debug:
203 //GN_PrintPacketContents ();
204
205 //Definitely a RecvBuffer, so can check if it is my ID:
206 //NOTE: shouldn't get here.
207 if (ntohl (GN_RecvBuffer.TimeInfo.ID) == GN_My.ID)
208 {
209 GN_ERROUT
210 ("Apparently I have several IP addresses and am contacting myself!");
211 return;
212 }
213
214 unsigned int ID = ntohl (GN_RecvBuffer.TimeInfo.ID);
215
216 //first see if this is an IP/Port I've heard from before:
217 //NOTE: Logically, this can prove it is a friend I know, but not
218 //that it is is a friend I don't know (multiple IP issue 20080113)
219 GN_Friend *curFriend =
220 GN_FriendList_GetFriend (ID, saddr_remote->sin_addr.s_addr,
221 saddr_remote->sin_port);
222
223 //we didn't know her, so see if she's a candidate for Friend:
224 if (NULL == curFriend)
225 {
226 curFriend =
227 GN_ProcessNewFriend (ID, saddr_remote->sin_addr.s_addr,
228 saddr_remote->sin_port);
229
230 //check to see if she really got added:
231 if (NULL == curFriend)
232 return;
233 }
234
235 GN_DBGOUT ("Got data from:");
236 GN_PrintAddressInfo (ID, saddr_remote->sin_addr.s_addr,
237 saddr_remote->sin_port);
238
239 switch (GN_RecvBuffer.msgtype)
240 {
241 case GN_MSGTYPE_TIMEINFO:
242 ++(curFriend->RecvCount);
243 curFriend->SendRecvTally = 0;
244 curFriend->ID = ntohl (GN_RecvBuffer.TimeInfo.ID);
245 curFriend->FriendCount = ntohs (GN_RecvBuffer.TimeInfo.FriendCount);
246 curFriend->RunningID = ntohs (GN_RecvBuffer.TimeInfo.RunningID);
247 curFriend->ScheduleFingerprint =
248 ntohs (GN_RecvBuffer.TimeInfo.ScheduleFingerprint);
249 curFriend->RunningSeniority =
250 ntohs (GN_RecvBuffer.TimeInfo.RunningSeniority);
251 curFriend->CurrentSampleCount =
252 ntohl (GN_RecvBuffer.TimeInfo.CurrentSampleCount);
253
254 //do time stuff:
255 GN_Time_ProcessIncomingData (curFriend);
256
257 //check invitation and add if not already here
258 GN_ProcessInviteData (ntohl (GN_RecvBuffer.TimeInfo.InviteID),
259 ntohl (GN_RecvBuffer.TimeInfo.InviteIP),
260 ntohs (GN_RecvBuffer.TimeInfo.InvitePort));
261 break;
262
263 default:
264 GN_DBGOUT ("Message type NOT PROCESSED");
265 break;
266 }
267 }
268
269 /////////////////////////////////////////////////////
270 //This just does the generic things all outgoing packets need:
GN_ProcessOutgoingData(GN_Friend * curFriend)271 void GN_ProcessOutgoingData (GN_Friend * curFriend)
272 {
273 if (NULL == curFriend)
274 return;
275
276 //dig up a random Friend to give receiver:
277 unsigned int InviteIP = 0;
278 unsigned short InvitePort = 0;
279 unsigned int InviteID = 0;
280 GN_Friend *randFriend = GN_FriendList_RandomFriend (curFriend);
281
282 if (NULL != randFriend)
283 {
284 InviteIP = randFriend->IP;
285 InvitePort = randFriend->Port;
286 InviteID = randFriend->ID;
287 }
288
289 GN_DBGOUT_INT ("Current friend count:", GN_My.FriendCount);
290
291 //Now do the critical timing stuff:
292 GN_Time_ProcessOutgoingData (curFriend);
293
294 //now put data in, setting proper byte order for network:
295 GN_SendBuffer.msgtype = GN_MSGTYPE_TIMEINFO;
296 GN_SendBuffer.TimeInfo.Time_DelayOnMe = htonl (curFriend->Time_DelayOnMe);
297 GN_SendBuffer.TimeInfo.FriendCount = htons (GN_My.FriendCount);
298 GN_SendBuffer.TimeInfo.InviteID = htonl (InviteID);
299 GN_SendBuffer.TimeInfo.InviteIP = htonl (InviteIP);
300 GN_SendBuffer.TimeInfo.InvitePort = htons (InvitePort);
301 GN_SendBuffer.TimeInfo.ID = htonl (GN_My.ID);
302 //now fill the Running data, set in GN_Running_ProcessOutgoingData:
303 GN_SendBuffer.TimeInfo.RunningID = htons (GN_My.RunningID);
304 GN_SendBuffer.TimeInfo.ScheduleFingerprint =
305 htons (GN_My.ScheduleFingerprint);
306 GN_SendBuffer.TimeInfo.RunningSeniority =
307 htons (curFriend->Time_Send.tv_sec - GN_My.RunningStartTime.tv_sec);
308 GN_SendBuffer.TimeInfo.CurrentSampleCount = htonl (GN_My.CurrentSampleCount);
309 GN_SendBuffer.TimeInfo.LoopCount = htons (GN_My.LoopCount);
310
311 //Now the actual send:
312 GN_Socket_SendMessage (GN_My.Socket, (char *) &GN_SendBuffer,
313 sizeof (GN_SendBuffer), curFriend->IP,
314 curFriend->Port);
315 }
316
317 ///////////////////////////////////////
318 //this is here simply to reduce code redundancy
GN_ProcessNewFriend(unsigned int ID,unsigned int IP,unsigned short Port)319 GN_Friend *GN_ProcessNewFriend (unsigned int ID, //MUST BE IN NET ORDER
320 unsigned int IP, //MUST BE IN NET ORDER
321 unsigned short Port) //MUST BE IN NET ORDER)
322 {
323 GN_Friend *curFriend = NULL;
324
325 //add her:
326 GN_DBGOUT ("Adding friend from:");
327 GN_PrintAddressInfo (ID, IP, Port);
328 curFriend = GN_FriendList_AddFriend (ID, IP, Port);
329
330 //check to see if she really got added:
331 if (NULL == curFriend)
332 return NULL;
333
334 //send him a quick greeting, as per Issue 20080112. NOTE:
335 //20080113: fixed bug here; was using the one CommBuffer to send
336 //BEFORE it had filed the data it recv'd. Solution: two commbuffs.
337 GN_ProcessOutgoingData (curFriend);
338 return curFriend;
339 }
340
341 ///////////////////////////////////////
342 //these incoming vars are in NET ORDER
343 //Philosophy: Invites are not added directly as friends. Instead, their
344 //address is sent to, and when it responds, it gets added. This is a
345 //security feature, since it would be absurdly easy to introduce crap
346 //into the FriendLists via addresses.
GN_ProcessInviteData(unsigned int InviteID,unsigned int InviteIP,unsigned short InvitePort)347 void GN_ProcessInviteData (unsigned int InviteID, //MUST BE IN NET ORDER
348 unsigned int InviteIP, //MUST BE IN NET ORDER
349 unsigned short InvitePort) //MUST BE IN NET ORDER
350 {
351 //see if it is invalid:
352 if (0 == InviteIP || 0 == InvitePort)
353 return;
354
355 //see if it is ME:
356 if (InviteID == GN_My.ID)
357 {
358 GN_DBGOUT ("Invite was to myself!");
359 return;
360 }
361
362 //see if I have that friend already:
363 GN_Friend *curFriend =
364 GN_FriendList_GetFriend (InviteID, InviteIP, InvitePort);
365
366 //if it is not a friend already, send her an Invite and forget about her:
367 if (NULL == curFriend)
368 {
369 GN_FriendList_Invite (InviteIP, InvitePort);
370 }
371 else
372 {
373 // GN_DBGOUT ("NOT adding Invited friend at:");
374 // GN_PrintAddressInfo (InviteID, InviteIP, InvitePort);
375 }
376 }
377
378 /////////////////////////////////////////////////////////////////////
379 //GN_init (): inits net subsystem, creates socket,
380 //sets blocking behavior
GN_init()381 int GN_init ()
382 {
383 memset (&GN_My, 0, sizeof (GN_Globals)); //precautionary
384 GN_My.Socket = 0; //Win32: unsigned int, UNIX: int
385 GN_My.IP = 0; //THIS IS IN NET ORDER
386 GN_My.Port = 0; //THIS IS IN NET ORDER
387 GN_My.ID = 0; //THIS IS IN NET ORDER
388 GN_My.RunningID = 0; //0 if not in a running
389 GN_My.ScheduleFingerprint = GN_ScheduleFingerprint;
390 GN_My.CurrentSampleCount = 0; //BB data, set in Running
391 GN_My.LoopCount = 0; //BB data, set in Running
392 GN_My.FriendCount = 0; //holds snapshots/estimates of current number of friends in linked list
393 GN_My.LoverCount = 0; //holds snapshots/estimates of current number of lovers in linked list
394 GN_My.FirstFriend = NULL;
395 GN_My.State = GNAURALNET_STOPPED;
396 GN_Time_ResetSeniority ();
397
398 #ifdef GNAURAL_WIN32
399 // initiate use of WINSOCK.DLL
400 WSADATA wsadata;
401 WORD wVer = MAKEWORD (1, 1);
402
403 if (WSAStartup (wVer, &wsadata) != 0)
404 {
405 GN_ERROUT ("Error occurred on WSAStartup()");
406 return GNAURALNET_FAILURE;
407 }
408 // Ensure the WINSOCK.DLL version is right
409 if (LOBYTE (wsadata.wVersion) != 1 || HIBYTE (wsadata.wVersion) != 1)
410 {
411 GN_ERROUT ("WINSOCK.DLL wrong version");
412 return GNAURALNET_FAILURE;
413 }
414 #endif
415
416 //create the one socket:
417 //NOTE: if port is already in use, this will fail. For now,
418 //it is assumed that two Gnaurals are trying to
419 //network on same machine. This is legal, but subsequent Gnaurals
420 //get any unused port and wont be known externally:
421 if (GNAURAL_IS_INVALID_SOCKET
422 (GN_My.Socket = GN_Socket_MakeUDP (GNAURALNET_PORT)))
423 {
424 perror ("Socket creation failed: ");
425 GN_DBGOUT ("Going to try socket creation on a different port:");
426 if (GNAURAL_IS_INVALID_SOCKET (GN_My.Socket = GN_Socket_MakeUDP (0)))
427 {
428 perror ("GN_Socket_MakeUDP failed again:");
429 GN_ERROUT ("Failed to start Gnauralnet");
430 return GNAURALNET_FAILURE;
431 }
432 }
433
434 //set local info globals:
435 GTimeVal curtime;
436
437 g_get_current_time (&curtime);
438 srand (curtime.tv_sec + curtime.tv_usec);
439 if (GNAURALNET_FAILURE == GN_Socket_SetLocalInfo ())
440 {
441 GN_ERROUT ("Couldn't get local address (shouldn't happen)");
442 return GNAURALNET_FAILURE;
443 }
444
445 GN_DBGOUT ("Socket Creation successful on:");
446 GN_PrintAddressInfo (GN_My.ID, GN_My.IP, GN_My.Port);
447
448 //set the socket blocking behavior:
449 //NOTE: using non-blocking sockets with select()
450 //and timeouts seems to be the best choice; for one thing,
451 //timeouts allow me to unblock when trying to close the socket:
452 GN_Socket_SetBlocking (GN_My.Socket, 0);
453 GN_DBGOUT ("Successfully started Gnauralnet");
454 return GNAURALNET_SUCCESS;
455 }
456
457 /////////////////////////////////////////////////////////////////////
458 //Note the handy G_USEC_PER_SEC
GN_sleep(unsigned int microsecs)459 void GN_sleep (unsigned int microsecs)
460 {
461 #ifdef GNAURAL_WIN32
462 Sleep (microsecs / 1000);
463 #else
464 usleep (microsecs);
465 #endif
466 }
467
468 /////////////////////////////////////////////////////////////////
469 //don't call this directly; call GN_stop()
GN_cleanup()470 void GN_cleanup ()
471 {
472 GN_DBGOUT ("Cleaning up gnauralnet");
473 GNAURAL_CLOSE_SOCKET (GN_My.Socket);
474 //GN_My.Socket = -1;
475 #ifdef GNAURAL_WIN32
476 WSACleanup ();
477 //GN_My.Socket = INVALID_SOCKET;
478 #endif
479 GN_PrintFriends ();
480 GN_FriendList_DeleteAll ();
481 }
482
483 /////////////////////////////////////////////////////////////////
484 //GN_start and GN_stop are all user must run.
485 //this sets GN_My.State to GNAURALNET_WAITINGTOSTOP to break
486 //any recv loops, then shuts down the socket
487 /////////////////////////////////////////////////////////////////
GN_stop()488 void GN_stop ()
489 {
490 if (GNAURALNET_RUNNING == GN_My.State)
491 {
492 GN_My.State = GNAURALNET_WAITINGTOSTOP;
493 int count = 4;
494
495 while (GNAURALNET_WAITINGTOSTOP == GN_My.State && --count)
496 {
497 GN_DBGOUT ("Waiting for gnauralnet shutdown...");
498 GN_sleep (500000);
499 }
500 if (0 == count)
501 GN_DBGOUT ("Forcing gnauralnet shutdown");
502 }
503 GN_DBGOUT ("Connection to GnauralNet was terminated");
504 GN_My.State = GNAURALNET_STOPPED;
505 GN_cleanup ();
506 }
507
508 /////////////////////////////////////////////////////
509 //incoming vars MUST be in net order
GN_PrintAddressInfo(unsigned int id,unsigned int ip,unsigned short port)510 void GN_PrintAddressInfo (unsigned int id, unsigned int ip,
511 unsigned short port)
512 {
513 if (0 == GN_DebugFlag)
514 {
515 return;
516 }
517 //start debug:
518 struct in_addr tmp_in_addr;
519
520 tmp_in_addr.s_addr = ip;
521 fprintf (stderr, "IP: %s \t Port: %d \t ID: %u\n", inet_ntoa (tmp_in_addr),
522 ntohs (port), id);
523 //end debug
524 }
525
526 /////////////////////////////////////////////////////
527 //For debugging:
GN_PrintPacketContents()528 void GN_PrintPacketContents ()
529 {
530 if (0 == GN_DebugFlag)
531 {
532 return;
533 }
534
535 //can do either Recv or Comm:
536 //GN_CommBuffer *buf = &GN_SendBuffer;
537 GN_CommBuffer *buf = &GN_RecvBuffer;
538
539 switch (buf->msgtype)
540 {
541 case GN_MSGTYPE_TIMEINFO:
542 fprintf (stderr, "=====TimeInfo:\n");
543 fprintf (stderr, " msgtype: %d\n", buf->TimeInfo.msgtype);
544 fprintf (stderr, " empty: %d\n", buf->TimeInfo.empty);
545 fprintf (stderr, " InvitePort: %d\n", ntohs (buf->TimeInfo.InvitePort));
546 fprintf (stderr, " InviteIP: %d\n", ntohl (buf->TimeInfo.InviteIP));
547 fprintf (stderr, " InviteID: %d\n", ntohl (buf->TimeInfo.InviteID));
548 fprintf (stderr, " ID: %d\n", ntohl (buf->TimeInfo.ID));
549 fprintf (stderr, " Time_DelayOnMe: %d\n",
550 ntohl (buf->TimeInfo.Time_DelayOnMe));
551 fprintf (stderr, " FriendCount: %d\n", ntohs (buf->TimeInfo.FriendCount));
552 fprintf (stderr, " RunningID: %d\n", ntohs (buf->TimeInfo.RunningID));
553 fprintf (stderr, " ScheduleFingerprint: %d\n",
554 ntohs (buf->TimeInfo.ScheduleFingerprint));
555 fprintf (stderr, " RunningSeniority: %d\n",
556 ntohs (buf->TimeInfo.RunningSeniority));
557 fprintf (stderr, " CurrentSampleCount: %d\n",
558 ntohl (buf->TimeInfo.CurrentSampleCount));
559 fprintf (stderr, " LoopCount: %d\n", ntohs (buf->TimeInfo.LoopCount));
560 break;
561 }
562 }
563
564 /////////////////////////////////////////////////////
565 //this is strictly a debugging tool
GN_PrintFriends()566 void GN_PrintFriends ()
567 {
568 if (0 == GN_DebugFlag)
569 {
570 return;
571 }
572
573 GN_Friend *curFriend = GN_My.FirstFriend;
574 struct in_addr tmp_in_addr;
575
576 GN_My.FriendCount = 0;
577 GN_My.LoverCount = 0;
578
579 while (curFriend != NULL)
580 {
581 ++GN_My.FriendCount;
582 if (0 != curFriend->RunningID && curFriend->RunningID == GN_My.RunningID)
583 {
584 ++GN_My.LoverCount;
585 }
586 tmp_in_addr.s_addr = curFriend->IP;
587 fprintf (stderr, "%d)IP:%s\tPort:%d\tID:%u\tOffset:%g\n",
588 GN_My.FriendCount, inet_ntoa (tmp_in_addr),
589 ntohs (curFriend->Port), curFriend->ID,
590 curFriend->Time_AverageOffset);
591 //all list passes need this:
592 curFriend = curFriend->NextFriend;
593 }
594 }
595