]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conn.c
- New source files "conn-zip.c" and "conn-zip.h".
[ngircd-alex.git] / src / ngircd / conn.c
1 /*
2  * ngIRCd -- The Next Generation IRC Daemon
3  * Copyright (c)2001,2002 by Alexander Barton (alex@barton.de)
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * Please read the file COPYING, README and AUTHORS for more information.
10  *
11  * Connection management
12  */
13
14
15 #define __conn_c__
16
17 #include "portab.h"
18
19 static char UNUSED id[] = "$Id: conn.c,v 1.112 2002/12/30 16:07:23 alex Exp $";
20
21 #include "imp.h"
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <string.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <time.h>
34 #include <netinet/in.h>
35
36 #ifdef HAVE_ARPA_INET_H
37 #include <arpa/inet.h>
38 #else
39 #define PF_INET AF_INET
40 #endif
41
42 #ifdef HAVE_STDINT_H
43 #include <stdint.h>                     /* u.a. fuer Mac OS X */
44 #endif
45
46 #include "defines.h"
47 #include "resolve.h"
48
49 #include "exp.h"
50 #include "conn.h"
51
52 #include "imp.h"
53 #include "ngircd.h"
54 #include "client.h"
55 #include "conf.h"
56 #include "conn-zip.h"
57 #include "log.h"
58 #include "parse.h"
59 #include "tool.h"
60
61 #include "exp.h"
62
63
64 #define SERVER_WAIT (NONE - 1)
65
66
67 LOCAL VOID Handle_Read PARAMS(( INT sock ));
68 LOCAL BOOLEAN Handle_Write PARAMS(( CONN_ID Idx ));
69 LOCAL VOID New_Connection PARAMS(( INT Sock ));
70 LOCAL CONN_ID Socket2Index PARAMS(( INT Sock ));
71 LOCAL VOID Read_Request PARAMS(( CONN_ID Idx ));
72 LOCAL BOOLEAN Try_Write PARAMS(( CONN_ID Idx ));
73 LOCAL BOOLEAN Handle_Buffer PARAMS(( CONN_ID Idx ));
74 LOCAL VOID Check_Connections PARAMS(( VOID ));
75 LOCAL VOID Check_Servers PARAMS(( VOID ));
76 LOCAL VOID Init_Conn_Struct PARAMS(( CONN_ID Idx ));
77 LOCAL BOOLEAN Init_Socket PARAMS(( INT Sock ));
78 LOCAL VOID New_Server PARAMS(( INT Server, CONN_ID Idx ));
79 LOCAL VOID Read_Resolver_Result PARAMS(( INT r_fd ));
80
81 LOCAL fd_set My_Listeners;
82 LOCAL fd_set My_Sockets;
83 LOCAL fd_set My_Connects;
84
85 LOCAL LONG WCounter;
86
87
88 GLOBAL VOID
89 Conn_Init( VOID )
90 {
91         /* Modul initialisieren: statische Strukturen "ausnullen". */
92
93         CONN_ID i;
94
95         /* Speicher fuer Verbindungs-Pool anfordern */
96         Pool_Size = CONNECTION_POOL;
97         if( Conf_MaxConnections > 0 )
98         {
99                 /* konfiguriertes Limit beachten */
100                 if( Pool_Size > Conf_MaxConnections ) Pool_Size = Conf_MaxConnections;
101         }
102         My_Connections = malloc( sizeof( CONNECTION ) * Pool_Size );
103         if( ! My_Connections )
104         {
105                 /* Speicher konnte nicht alloziert werden! */
106                 Log( LOG_EMERG, "Can't allocate memory! [Conn_Init]" );
107                 exit( 1 );
108         }
109         Log( LOG_DEBUG, "Allocted connection pool for %d items (%ld bytes).", Pool_Size, sizeof( CONNECTION ) * Pool_Size );
110
111         /* zu Beginn haben wir keine Verbindungen */
112         FD_ZERO( &My_Listeners );
113         FD_ZERO( &My_Sockets );
114         FD_ZERO( &My_Connects );
115
116         /* Groesster File-Descriptor fuer select() */
117         Conn_MaxFD = 0;
118
119         /* Connection-Struktur initialisieren */
120         for( i = 0; i < Pool_Size; i++ ) Init_Conn_Struct( i );
121
122         /* Global write counter */
123         WCounter = 0;
124 } /* Conn_Init */
125
126
127 GLOBAL VOID
128 Conn_Exit( VOID )
129 {
130         /* Modul abmelden: alle noch offenen Connections
131          * schliessen und freigeben. */
132
133         CONN_ID idx;
134         INT i;
135
136         /* Sockets schliessen */
137         Log( LOG_DEBUG, "Shutting down all connections ..." );
138         for( i = 0; i < Conn_MaxFD + 1; i++ )
139         {
140                 if( FD_ISSET( i, &My_Sockets ))
141                 {
142                         for( idx = 0; idx < Pool_Size; idx++ )
143                         {
144                                 if( My_Connections[idx].sock == i ) break;
145                         }
146                         if( FD_ISSET( i, &My_Listeners ))
147                         {
148                                 close( i );
149                                 Log( LOG_DEBUG, "Listening socket %d closed.", i );
150                         }
151                         else if( FD_ISSET( i, &My_Connects ))
152                         {
153                                 close( i );
154                                 Log( LOG_DEBUG, "Connection %d closed during creation (socket %d).", idx, i );
155                         }
156                         else if( idx < Pool_Size )
157                         {
158                                 if( NGIRCd_SignalRestart ) Conn_Close( idx, NULL, "Server going down (restarting)", TRUE );
159                                 else Conn_Close( idx, NULL, "Server going down", TRUE );
160                         }
161                         else
162                         {
163                                 Log( LOG_WARNING, "Closing unknown connection %d ...", i );
164                                 close( i );
165                         }
166                 }
167         }
168         
169         free( My_Connections );
170         My_Connections = NULL;
171         Pool_Size = 0;
172 } /* Conn_Exit */
173
174
175 GLOBAL INT
176 Conn_InitListeners( VOID )
177 {
178         /* Initialize ports on which the server should accept connections */
179
180         INT created, i;
181
182         created = 0;
183         for( i = 0; i < Conf_ListenPorts_Count; i++ )
184         {
185                 if( Conn_NewListener( Conf_ListenPorts[i] )) created++;
186                 else Log( LOG_ERR, "Can't listen on port %u!", Conf_ListenPorts[i] );
187         }
188         return created;
189 } /* Conn_InitListeners */
190
191
192 GLOBAL VOID
193 Conn_ExitListeners( VOID )
194 {
195         /* Close down all listening sockets */
196
197         INT i;
198
199         Log( LOG_INFO, "Shutting down all listening sockets ..." );
200         for( i = 0; i < Conn_MaxFD + 1; i++ )
201         {
202                 if( FD_ISSET( i, &My_Sockets ) && FD_ISSET( i, &My_Listeners ))
203                 {
204                         close( i );
205                         Log( LOG_DEBUG, "Listening socket %d closed.", i );
206                 }
207         }
208 } /* Conn_ExitListeners */
209
210
211 GLOBAL BOOLEAN
212 Conn_NewListener( CONST UINT Port )
213 {
214         /* Create new listening socket on specified port */
215
216         struct sockaddr_in addr;
217         INT sock;
218
219         /* Server-"Listen"-Socket initialisieren */
220         memset( &addr, 0, sizeof( addr ));
221         addr.sin_family = AF_INET;
222         addr.sin_port = htons( Port );
223         addr.sin_addr.s_addr = htonl( INADDR_ANY );
224
225         /* Socket erzeugen */
226         sock = socket( PF_INET, SOCK_STREAM, 0);
227         if( sock < 0 )
228         {
229                 Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno ));
230                 return FALSE;
231         }
232
233         if( ! Init_Socket( sock )) return FALSE;
234
235         /* an Port binden */
236         if( bind( sock, (struct sockaddr *)&addr, (socklen_t)sizeof( addr )) != 0 )
237         {
238                 Log( LOG_CRIT, "Can't bind socket: %s!", strerror( errno ));
239                 close( sock );
240                 return FALSE;
241         }
242
243         /* in "listen mode" gehen :-) */
244         if( listen( sock, 10 ) != 0 )
245         {
246                 Log( LOG_CRIT, "Can't listen on soecket: %s!", strerror( errno ));
247                 close( sock );
248                 return FALSE;
249         }
250
251         /* Neuen Listener in Strukturen einfuegen */
252         FD_SET( sock, &My_Listeners );
253         FD_SET( sock, &My_Sockets );
254
255         if( sock > Conn_MaxFD ) Conn_MaxFD = sock;
256
257         Log( LOG_INFO, "Now listening on port %d (socket %d).", Port, sock );
258
259         return TRUE;
260 } /* Conn_NewListener */
261
262
263 GLOBAL VOID
264 Conn_Handler( VOID )
265 {
266         /* "Hauptschleife": Aktive Verbindungen ueberwachen. Folgende Aktionen
267          * werden dabei durchgefuehrt, bis der Server terminieren oder neu
268          * starten soll:
269          *
270          *  - neue Verbindungen annehmen,
271          *  - Server-Verbindungen aufbauen,
272          *  - geschlossene Verbindungen loeschen,
273          *  - volle Schreibpuffer versuchen zu schreiben,
274          *  - volle Lesepuffer versuchen zu verarbeiten,
275          *  - Antworten von Resolver Sub-Prozessen annehmen.
276          */
277
278         fd_set read_sockets, write_sockets;
279         struct timeval tv;
280         time_t start, t;
281         CONN_ID i, idx;
282         BOOLEAN timeout;
283
284         start = time( NULL );
285         while(( ! NGIRCd_SignalQuit ) && ( ! NGIRCd_SignalRestart ))
286         {
287                 timeout = TRUE;
288
289                 /* Should the configuration be reloaded? */
290                 if( NGIRCd_SignalRehash ) NGIRCd_Rehash( );
291
292                 /* Check configured servers and established links */
293                 Check_Servers( );
294                 Check_Connections( );
295
296                 /* noch volle Lese-Buffer suchen */
297                 for( i = 0; i < Pool_Size; i++ )
298                 {
299                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].rdatalen > 0 ))
300                         {
301                                 /* Kann aus dem Buffer noch ein Befehl extrahiert werden? */
302                                 if( Handle_Buffer( i )) timeout = FALSE;
303                         }
304                 }
305
306                 /* noch volle Schreib-Puffer suchen */
307                 FD_ZERO( &write_sockets );
308                 for( i = 0; i < Pool_Size; i++ )
309                 {
310 #ifdef USE_ZLIB
311                         if(( My_Connections[i].sock > NONE ) && (( My_Connections[i].wdatalen > 0 ) || ( My_Connections[i].zip.wdatalen > 0 )))
312 #else
313                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].wdatalen > 0 ))
314 #endif
315                         {
316                                 /* Socket der Verbindung in Set aufnehmen */
317                                 FD_SET( My_Connections[i].sock, &write_sockets );
318                         }
319                 }
320
321                 /* Sockets mit im Aufbau befindlichen ausgehenden Verbindungen suchen */
322                 for( i = 0; i < Pool_Size; i++ )
323                 {
324                         if(( My_Connections[i].sock > NONE ) && ( FD_ISSET( My_Connections[i].sock, &My_Connects ))) FD_SET( My_Connections[i].sock, &write_sockets );
325                 }
326
327                 /* von welchen Sockets koennte gelesen werden? */
328                 t = time( NULL );
329                 read_sockets = My_Sockets;
330                 for( i = 0; i < Pool_Size; i++ )
331                 {
332                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].host[0] == '\0' ))
333                         {
334                                 /* Hier muss noch auf den Resolver Sub-Prozess gewartet werden */
335                                 FD_CLR( My_Connections[i].sock, &read_sockets );
336                         }
337                         if(( My_Connections[i].sock > NONE ) && ( FD_ISSET( My_Connections[i].sock, &My_Connects )))
338                         {
339                                 /* Hier laeuft noch ein asyncrones connect() */
340                                 FD_CLR( My_Connections[i].sock, &read_sockets );
341                         }
342                         if( My_Connections[i].delaytime > t )
343                         {
344                                 /* Fuer die Verbindung ist eine "Penalty-Zeit" gesetzt */
345                                 FD_CLR( My_Connections[i].sock, &read_sockets );
346                         }
347                 }
348                 for( i = 0; i < Conn_MaxFD + 1; i++ )
349                 {
350                         /* Pipes von Resolver Sub-Prozessen aufnehmen */
351                         if( FD_ISSET( i, &Resolver_FDs ))
352                         {
353                                 FD_SET( i, &read_sockets );
354                         }
355                 }
356
357                 /* Timeout initialisieren */
358                 tv.tv_usec = 0;
359                 if( timeout ) tv.tv_sec = TIME_RES;
360                 else tv.tv_sec = 0;
361                 
362                 /* Auf Aktivitaet warten */
363                 i = select( Conn_MaxFD + 1, &read_sockets, &write_sockets, NULL, &tv );
364                 if( i == 0 )
365                 {
366                         /* keine Veraenderung an den Sockets */
367                         continue;
368                 }
369                 if( i == -1 )
370                 {
371                         /* Fehler (z.B. Interrupt) */
372                         if( errno != EINTR )
373                         {
374                                 Log( LOG_EMERG, "Conn_Handler(): select(): %s!", strerror( errno ));
375                                 Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
376                                 exit( 1 );
377                         }
378                         continue;
379                 }
380
381                 /* Koennen Daten geschrieben werden? */
382                 for( i = 0; i < Conn_MaxFD + 1; i++ )
383                 {
384                         if( ! FD_ISSET( i, &write_sockets )) continue;
385
386                         /* Es kann geschrieben werden ... */
387                         idx = Socket2Index( i );
388                         if( idx == NONE ) continue;
389                         
390                         if( ! Handle_Write( idx ))
391                         {
392                                 /* Fehler beim Schreiben! Diesen Socket nun
393                                  * auch aus dem Read-Set entfernen: */
394                                 FD_CLR( i, &read_sockets );
395                         }
396                 }
397
398                 /* Daten zum Lesen vorhanden? */
399                 for( i = 0; i < Conn_MaxFD + 1; i++ )
400                 {
401                         if( FD_ISSET( i, &read_sockets )) Handle_Read( i );
402                 }
403         }
404
405         if( NGIRCd_SignalQuit ) Log( LOG_NOTICE|LOG_snotice, "Server going down NOW!" );
406         else if( NGIRCd_SignalRestart ) Log( LOG_NOTICE|LOG_snotice, "Server restarting NOW!" );
407 } /* Conn_Handler */
408
409
410 #ifdef PROTOTYPES
411 GLOBAL BOOLEAN
412 Conn_WriteStr( CONN_ID Idx, CHAR *Format, ... )
413 #else
414 GLOBAL BOOLEAN
415 Conn_WriteStr( Idx, Format, va_alist )
416 CONN_ID Idx;
417 CHAR *Format;
418 va_dcl
419 #endif
420 {
421         /* String in Socket schreiben. CR+LF wird von dieser Funktion
422          * automatisch angehaengt. Im Fehlerfall wird dir Verbindung
423          * getrennt und FALSE geliefert. */
424
425         CHAR buffer[COMMAND_LEN];
426         BOOLEAN ok;
427         va_list ap;
428
429         assert( Idx > NONE );
430         assert( Format != NULL );
431
432 #ifdef PROTOTYPES
433         va_start( ap, Format );
434 #else
435         va_start( ap );
436 #endif
437         if( vsnprintf( buffer, COMMAND_LEN - 2, Format, ap ) == COMMAND_LEN - 2 )
438         {
439                 Log( LOG_CRIT, "Text too long to send (connection %d)!", Idx );
440                 Conn_Close( Idx, "Text too long to send!", NULL, FALSE );
441                 return FALSE;
442         }
443
444 #ifdef SNIFFER
445         if( NGIRCd_Sniffer ) Log( LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer );
446 #endif
447
448         strlcat( buffer, "\r\n", sizeof( buffer ));
449         ok = Conn_Write( Idx, buffer, strlen( buffer ));
450         My_Connections[Idx].msg_out++;
451
452         va_end( ap );
453         return ok;
454 } /* Conn_WriteStr */
455
456
457 GLOBAL BOOLEAN
458 Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
459 {
460         /* Daten in Socket schreiben. Bei "fatalen" Fehlern wird
461          * der Client disconnectiert und FALSE geliefert. */
462
463         assert( Idx > NONE );
464         assert( Data != NULL );
465         assert( Len > 0 );
466
467         /* Ist der entsprechende Socket ueberhaupt noch offen? In einem
468          * "Handler-Durchlauf" kann es passieren, dass dem nicht mehr so
469          * ist, wenn einer von mehreren Conn_Write()'s fehlgeschlagen ist.
470          * In diesem Fall wird hier einfach ein Fehler geliefert. */
471         if( My_Connections[Idx].sock <= NONE )
472         {
473                 Log( LOG_DEBUG, "Skipped write on closed socket (connection %d).", Idx );
474                 return FALSE;
475         }
476
477         /* Pruefen, ob im Schreibpuffer genuegend Platz ist. Ziel ist es,
478          * moeglichts viel im Puffer zu haben und _nicht_ gleich alles auf den
479          * Socket zu schreiben (u.a. wg. Komprimierung). */
480         if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
481         {
482                 /* Der Puffer ist dummerweise voll. Jetzt versuchen, den Puffer
483                  * zu schreiben, wenn das nicht klappt, haben wir ein Problem ... */
484                 if( ! Try_Write( Idx )) return FALSE;
485
486                 /* nun neu pruefen: */
487                 if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
488                 {
489                         Log( LOG_NOTICE, "Write buffer overflow (connection %d)!", Idx );
490                         Conn_Close( Idx, "Write buffer overflow!", NULL, FALSE );
491                         return FALSE;
492                 }
493         }
494
495 #ifdef USE_ZLIB
496         if( My_Connections[Idx].options & CONN_ZIP )
497         {
498                 /* Daten komprimieren und in Puffer kopieren */
499                 if( ! Zip_Buffer( Idx, Data, Len )) return FALSE;
500         }
501         else
502 #endif
503         {
504                 /* Daten in Puffer kopieren */
505                 memcpy( My_Connections[Idx].wbuf + My_Connections[Idx].wdatalen, Data, Len );
506                 My_Connections[Idx].wdatalen += Len;
507                 My_Connections[Idx].bytes_out += Len;
508         }
509
510         /* Adjust global write counter */
511         WCounter += Len;
512
513         return TRUE;
514 } /* Conn_Write */
515
516
517 GLOBAL VOID
518 Conn_Close( CONN_ID Idx, CHAR *LogMsg, CHAR *FwdMsg, BOOLEAN InformClient )
519 {
520         /* Close connection. Open pipes of asyncronous resolver
521          * sub-processes are closed down. */
522
523         CLIENT *c;
524         DOUBLE in_k, out_k;
525 #ifdef USE_ZLIB
526         DOUBLE in_z_k, out_z_k;
527         INT in_p, out_p;
528 #endif
529
530         assert( Idx > NONE );
531         assert( My_Connections[Idx].sock > NONE );
532
533         /* Search client, if any */
534         c = Client_GetFromConn( Idx );
535
536         /* Should the client be informed? */
537         if( InformClient )
538         {
539 #ifndef STRICT_RFC
540                 /* Send statistics to client if registered as user: */
541                 if(( c != NULL ) && ( Client_Type( c ) == CLIENT_USER ))
542                 {
543                         Conn_WriteStr( Idx, "NOTICE %s :%sConnection statistics: client %.1f kb, server %.1f kb.", Client_ThisServer( ), NOTICE_TXTPREFIX, (DOUBLE)My_Connections[Idx].bytes_in / 1024,  (DOUBLE)My_Connections[Idx].bytes_out / 1024 );
544                 }
545 #endif
546
547                 /* Send ERROR to client (see RFC!) */
548                 if( FwdMsg ) Conn_WriteStr( Idx, "ERROR :%s", FwdMsg );
549                 else Conn_WriteStr( Idx, "ERROR :Closing connection." );
550                 if( My_Connections[Idx].sock == NONE ) return;
551         }
552
553         /* Try to write out the write buffer */
554         Try_Write( Idx );
555
556         /* Shut down socket */
557         if( close( My_Connections[Idx].sock ) != 0 )
558         {
559                 /* Oops, we can't close the socket!? This is fatal! */
560                 Log( LOG_EMERG, "Error closing connection %d (socket %d) with %s:%d - %s!", Idx, My_Connections[Idx].sock, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
561                 Log( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE );
562                 exit( 1 );
563         }
564
565         /* Mark socket as invalid: */
566         FD_CLR( My_Connections[Idx].sock, &My_Sockets );
567         FD_CLR( My_Connections[Idx].sock, &My_Connects );
568         My_Connections[Idx].sock = NONE;
569
570         /* If there is still a client, unregister it now */
571         if( c ) Client_Destroy( c, LogMsg, FwdMsg, TRUE );
572
573         /* Calculate statistics and log information */
574         in_k = (DOUBLE)My_Connections[Idx].bytes_in / 1024;
575         out_k = (DOUBLE)My_Connections[Idx].bytes_out / 1024;
576 #ifdef USE_ZLIB
577         if( My_Connections[Idx].options & CONN_ZIP )
578         {
579                 in_z_k = (DOUBLE)My_Connections[Idx].zip.bytes_in / 1024;
580                 out_z_k = (DOUBLE)My_Connections[Idx].zip.bytes_out / 1024;
581                 in_p = (INT)(( in_k * 100 ) / in_z_k );
582                 out_p = (INT)(( out_k * 100 ) / out_z_k );
583                 Log( LOG_INFO, "Connection %d with %s:%d closed (in: %.1fk/%.1fk/%d%%, out: %.1fk/%.1fk/%d%%).", Idx, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port ), in_k, in_z_k, in_p, out_k, out_z_k, out_p );
584         }
585         else
586 #endif
587         {
588                 Log( LOG_INFO, "Connection %d with %s:%d closed (in: %.1fk, out: %.1fk).", Idx, My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port ), in_k, out_k );
589         }
590
591         /* Is there a resolver sub-process running? */
592         if( My_Connections[Idx].res_stat )
593         {
594                 /* Free resolver structures */
595                 FD_CLR( My_Connections[Idx].res_stat->pipe[0], &Resolver_FDs );
596                 close( My_Connections[Idx].res_stat->pipe[0] );
597                 close( My_Connections[Idx].res_stat->pipe[1] );
598                 free( My_Connections[Idx].res_stat );
599         }
600
601         /* Servers: Modify time of next connect attempt? */
602         Conf_UnsetServer( Idx );
603
604 #ifdef USE_ZLIB
605         /* Clean up zlib, if link was compressed */
606         if( Conn_Options( Idx ) & CONN_ZIP )
607         {
608                 inflateEnd( &My_Connections[Idx].zip.in );
609                 deflateEnd( &My_Connections[Idx].zip.out );
610         }
611 #endif
612
613         /* Clean up connection structure (=free it) */
614         Init_Conn_Struct( Idx );
615 } /* Conn_Close */
616
617
618 GLOBAL VOID
619 Conn_UpdateIdle( CONN_ID Idx )
620 {
621         /* Idle-Timer zuruecksetzen */
622
623         assert( Idx > NONE );
624         My_Connections[Idx].lastprivmsg = time( NULL );
625 }
626
627
628 GLOBAL time_t
629 Conn_GetIdle( CONN_ID Idx )
630 {
631         /* Idle-Time einer Verbindung liefern (in Sekunden) */
632
633         assert( Idx > NONE );
634         return time( NULL ) - My_Connections[Idx].lastprivmsg;
635 } /* Conn_GetIdle */
636
637
638 GLOBAL time_t
639 Conn_LastPing( CONN_ID Idx )
640 {
641         /* Zeitpunkt des letzten PING liefern */
642
643         assert( Idx > NONE );
644         return My_Connections[Idx].lastping;
645 } /* Conn_LastPing */
646
647
648 GLOBAL VOID
649 Conn_SetPenalty( CONN_ID Idx, time_t Seconds )
650 {
651         /* Penalty-Delay fuer eine Verbindung (in Sekunden) setzen;
652          * waehrend dieser Zeit wird der entsprechende Socket vom Server
653          * bei Lese-Operationen komplett ignoriert. Der Delay kann mit
654          * dieser Funktion nur erhoeht, nicht aber verringert werden. */
655         
656         time_t t;
657         
658         assert( Idx > NONE );
659         assert( Seconds >= 0 );
660         
661         t = time( NULL ) + Seconds;
662         if( t > My_Connections[Idx].delaytime ) My_Connections[Idx].delaytime = t;
663 } /* Conn_SetPenalty */
664
665
666 GLOBAL VOID
667 Conn_ResetPenalty( CONN_ID Idx )
668 {
669         assert( Idx > NONE );
670         My_Connections[Idx].delaytime = 0;
671 } /* Conn_ResetPenalty */
672
673
674 GLOBAL VOID
675 Conn_ClearFlags( VOID )
676 {
677         /* Alle Connection auf "nicht-markiert" setzen */
678
679         CONN_ID i;
680
681         for( i = 0; i < Pool_Size; i++ ) My_Connections[i].flag = 0;
682 } /* Conn_ClearFlags */
683
684
685 GLOBAL INT
686 Conn_Flag( CONN_ID Idx )
687 {
688         /* Ist eine Connection markiert (TRUE) oder nicht? */
689
690         assert( Idx > NONE );
691         return My_Connections[Idx].flag;
692 } /* Conn_Flag */
693
694
695 GLOBAL VOID
696 Conn_SetFlag( CONN_ID Idx, INT Flag )
697 {
698         /* Connection markieren */
699
700         assert( Idx > NONE );
701         My_Connections[Idx].flag = Flag;
702 } /* Conn_SetFlag */
703
704
705 GLOBAL CONN_ID
706 Conn_First( VOID )
707 {
708         /* Connection-Struktur der ersten Verbindung liefern;
709          * Ist keine Verbindung vorhanden, wird NONE geliefert. */
710
711         CONN_ID i;
712         
713         for( i = 0; i < Pool_Size; i++ )
714         {
715                 if( My_Connections[i].sock != NONE ) return i;
716         }
717         return NONE;
718 } /* Conn_First */
719
720
721 GLOBAL CONN_ID
722 Conn_Next( CONN_ID Idx )
723 {
724         /* Naechste Verbindungs-Struktur liefern; existiert keine
725          * weitere, so wird NONE geliefert. */
726
727         CONN_ID i = NONE;
728
729         assert( Idx > NONE );
730         
731         for( i = Idx + 1; i < Pool_Size; i++ )
732         {
733                 if( My_Connections[i].sock != NONE ) return i;
734         }
735         return NONE;
736 } /* Conn_Next */
737
738
739 GLOBAL VOID
740 Conn_SetOption( CONN_ID Idx, INT Option )
741 {
742         /* Option fuer Verbindung setzen.
743          * Initial sind alle Optionen _nicht_ gesetzt. */
744
745         assert( Idx > NONE );
746         assert( Option != 0 );
747
748         My_Connections[Idx].options |= Option;
749 } /* Conn_SetOption */
750
751
752 GLOBAL VOID
753 Conn_UnsetOption( CONN_ID Idx, INT Option )
754 {
755         /* Option fuer Verbindung loeschen */
756
757         assert( Idx > NONE );
758         assert( Option != 0 );
759
760         My_Connections[Idx].options &= ~Option;
761 } /* Conn_UnsetOption */
762
763
764 GLOBAL INT
765 Conn_Options( CONN_ID Idx )
766 {
767         assert( Idx > NONE );
768         return My_Connections[Idx].options;
769 } /* Conn_Options */
770
771
772 GLOBAL time_t
773 Conn_StartTime( CONN_ID Idx )
774 {
775         /* Zeitpunkt des Link-Starts liefern (in Sekunden) */
776
777         assert( Idx > NONE );
778         return My_Connections[Idx].starttime;
779 } /* Conn_Uptime */
780
781
782 GLOBAL INT
783 Conn_SendQ( CONN_ID Idx )
784 {
785         /* Laenge der Daten im Schreibbuffer liefern */
786
787         assert( Idx > NONE );
788 #ifdef USE_ZLIB
789         if( My_Connections[Idx].options & CONN_ZIP ) return My_Connections[Idx].zip.wdatalen;
790         else
791 #endif
792         return My_Connections[Idx].wdatalen;
793 } /* Conn_SendQ */
794
795
796 GLOBAL LONG
797 Conn_SendMsg( CONN_ID Idx )
798 {
799         /* Anzahl gesendeter Nachrichten liefern */
800
801         assert( Idx > NONE );
802         return My_Connections[Idx].msg_out;
803 } /* Conn_SendMsg */
804
805
806 GLOBAL LONG
807 Conn_SendBytes( CONN_ID Idx )
808 {
809         /* Anzahl gesendeter Bytes (unkomprimiert) liefern */
810
811         assert( Idx > NONE );
812         return My_Connections[Idx].bytes_out;
813 } /* Conn_SendBytes */
814
815
816 GLOBAL INT
817 Conn_RecvQ( CONN_ID Idx )
818 {
819         /* Laenge der Daten im Lesebuffer liefern */
820
821         assert( Idx > NONE );
822 #ifdef USE_ZLIB
823         if( My_Connections[Idx].options & CONN_ZIP ) return My_Connections[Idx].zip.rdatalen;
824         else
825 #endif
826         return My_Connections[Idx].rdatalen;
827 } /* Conn_RecvQ */
828
829
830 GLOBAL LONG
831 Conn_RecvMsg( CONN_ID Idx )
832 {
833         /* Anzahl empfangener Nachrichten liefern */
834
835         assert( Idx > NONE );
836         return My_Connections[Idx].msg_in;
837 } /* Conn_RecvMsg */
838
839
840 GLOBAL LONG
841 Conn_RecvBytes( CONN_ID Idx )
842 {
843         /* Anzahl empfangener Bytes (unkomprimiert) liefern */
844
845         assert( Idx > NONE );
846         return My_Connections[Idx].bytes_in;
847 } /* Conn_RecvBytes */
848
849
850 GLOBAL VOID
851 Conn_ResetWCounter( VOID )
852 {
853         WCounter = 0;
854 } /* Conn_ResetWCounter */
855
856
857 GLOBAL LONG
858 Conn_WCounter( VOID )
859 {
860         return WCounter;
861 } /* Conn_WCounter */
862
863
864 LOCAL BOOLEAN
865 Try_Write( CONN_ID Idx )
866 {
867         /* Versuchen, Daten aus dem Schreib-Puffer in den Socket zu
868          * schreiben. TRUE wird geliefert, wenn entweder keine Daten
869          * zum Versenden vorhanden sind oder erfolgreich bearbeitet
870          * werden konnten. Im Fehlerfall wird FALSE geliefert und
871          * die Verbindung geschlossen. */
872
873         fd_set write_socket;
874         struct timeval tv;
875
876         assert( Idx > NONE );
877         assert( My_Connections[Idx].sock > NONE );
878
879         /* sind ueberhaupt Daten vorhanden? */
880 #ifdef USE_ZLIB
881         if(( ! My_Connections[Idx].wdatalen > 0 ) && ( ! My_Connections[Idx].zip.wdatalen )) return TRUE;
882 #else
883         if( ! My_Connections[Idx].wdatalen > 0 ) return TRUE;
884 #endif
885
886         /* Timeout initialisieren: 0 Sekunden, also nicht blockieren */
887         tv.tv_sec = 0; tv.tv_usec = 0;
888
889         FD_ZERO( &write_socket );
890         FD_SET( My_Connections[Idx].sock, &write_socket );
891         if( select( My_Connections[Idx].sock + 1, NULL, &write_socket, NULL, &tv ) == -1 )
892         {
893                 /* Fehler! */
894                 if( errno != EINTR )
895                 {
896                         Log( LOG_ALERT, "Try_Write(): select() failed: %s (con=%d, sock=%d)!", strerror( errno ), Idx, My_Connections[Idx].sock );
897                         Conn_Close( Idx, "Server error!", NULL, FALSE );
898                         return FALSE;
899                 }
900         }
901
902         if( FD_ISSET( My_Connections[Idx].sock, &write_socket )) return Handle_Write( Idx );
903         else return TRUE;
904 } /* Try_Write */
905
906
907 LOCAL VOID
908 Handle_Read( INT Sock )
909 {
910         /* Aktivitaet auf einem Socket verarbeiten:
911          *  - neue Clients annehmen,
912          *  - Daten von Clients verarbeiten,
913          *  - Resolver-Rueckmeldungen annehmen. */
914
915         CONN_ID idx;
916
917         assert( Sock > NONE );
918
919         if( FD_ISSET( Sock, &My_Listeners ))
920         {
921                 /* es ist einer unserer Listener-Sockets: es soll
922                  * also eine neue Verbindung aufgebaut werden. */
923
924                 New_Connection( Sock );
925         }
926         else if( FD_ISSET( Sock, &Resolver_FDs ))
927         {
928                 /* Rueckmeldung von einem Resolver Sub-Prozess */
929
930                 Read_Resolver_Result( Sock );
931         }
932         else
933         {
934                 /* Ein Client Socket: entweder ein User oder Server */
935
936                 idx = Socket2Index( Sock );
937                 if( idx > NONE ) Read_Request( idx );
938         }
939 } /* Handle_Read */
940
941
942 LOCAL BOOLEAN
943 Handle_Write( CONN_ID Idx )
944 {
945         /* Daten aus Schreibpuffer versenden bzw. Connection aufbauen */
946
947         INT len, res, err;
948
949         assert( Idx > NONE );
950         assert( My_Connections[Idx].sock > NONE );
951
952         if( FD_ISSET( My_Connections[Idx].sock, &My_Connects ))
953         {
954                 /* es soll nichts geschrieben werden, sondern ein
955                  * connect() hat ein Ergebnis geliefert */
956
957                 FD_CLR( My_Connections[Idx].sock, &My_Connects );
958
959                 /* Ergebnis des connect() ermitteln */
960                 len = sizeof( err );
961                 res = getsockopt( My_Connections[Idx].sock, SOL_SOCKET, SO_ERROR, &err, &len );
962                 assert( len == sizeof( err ));
963
964                 /* Fehler aufgetreten? */
965                 if(( res != 0 ) || ( err != 0 ))
966                 {
967                         /* Fehler! */
968                         if( res != 0 ) Log( LOG_CRIT, "getsockopt (connection %d): %s!", Idx, strerror( errno ));
969                         else Log( LOG_CRIT, "Can't connect socket to \"%s:%d\" (connection %d): %s!", My_Connections[Idx].host, Conf_Server[Conf_GetServer( Idx )].port, Idx, strerror( err ));
970
971                         /* Socket etc. pp. aufraeumen */
972                         FD_CLR( My_Connections[Idx].sock, &My_Sockets );
973                         close( My_Connections[Idx].sock );
974                         Init_Conn_Struct( Idx );
975
976                         /* Bei Server-Verbindungen lasttry-Zeitpunkt auf "jetzt" setzen */
977                         Conf_Server[Conf_GetServer( Idx )].lasttry = time( NULL );
978                         Conf_UnsetServer( Idx );
979
980                         return FALSE;
981                 }
982                 Log( LOG_DEBUG, "Connection %d with \"%s:%d\" established, now sendig PASS and SERVER ...", Idx, My_Connections[Idx].host, Conf_Server[Conf_GetServer( Idx )].port );
983
984                 /* PASS und SERVER verschicken */
985                 Conn_WriteStr( Idx, "PASS %s %s", Conf_Server[Conf_GetServer( Idx )].pwd_out, NGIRCd_ProtoID );
986                 return Conn_WriteStr( Idx, "SERVER %s :%s", Conf_ServerName, Conf_ServerInfo );
987         }
988
989 #ifdef USE_ZLIB
990         /* Schreibpuffer leer, aber noch Daten im Kompressionsbuffer?
991          * Dann muss dieser nun geflushed werden! */
992         if( My_Connections[Idx].wdatalen == 0 ) Zip_Flush( Idx );
993 #endif
994
995         assert( My_Connections[Idx].wdatalen > 0 );
996
997         /* Daten schreiben */
998         len = send( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen, 0 );
999         if( len < 0 )
1000         {
1001                 /* Operation haette Socket "nur" blockiert ... */
1002                 if( errno == EAGAIN ) return TRUE;
1003
1004                 /* Oops, ein Fehler! */
1005                 Log( LOG_ERR, "Write error on connection %d (socket %d): %s!", Idx, My_Connections[Idx].sock, strerror( errno ));
1006                 Conn_Close( Idx, "Write error!", NULL, FALSE );
1007                 return FALSE;
1008         }
1009
1010         /* Puffer anpassen */
1011         My_Connections[Idx].wdatalen -= len;
1012         memmove( My_Connections[Idx].wbuf, My_Connections[Idx].wbuf + len, My_Connections[Idx].wdatalen );
1013
1014         return TRUE;
1015 } /* Handle_Write */
1016
1017
1018 LOCAL VOID
1019 New_Connection( INT Sock )
1020 {
1021         /* Neue Client-Verbindung von Listen-Socket annehmen und
1022          * CLIENT-Struktur anlegen. */
1023
1024         struct sockaddr_in new_addr;
1025         INT new_sock, new_sock_len;
1026         RES_STAT *s;
1027         CONN_ID idx;
1028         CLIENT *c;
1029         POINTER *ptr;
1030         LONG new_size;
1031
1032         assert( Sock > NONE );
1033
1034         /* Connection auf Listen-Socket annehmen */
1035         new_sock_len = sizeof( new_addr );
1036         new_sock = accept( Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len );
1037         if( new_sock < 0 )
1038         {
1039                 Log( LOG_CRIT, "Can't accept connection: %s!", strerror( errno ));
1040                 return;
1041         }
1042
1043         /* Socket initialisieren */
1044         Init_Socket( new_sock );
1045
1046         /* Freie Connection-Struktur suchen */
1047         for( idx = 0; idx < Pool_Size; idx++ ) if( My_Connections[idx].sock == NONE ) break;
1048         if( idx >= Pool_Size )
1049         {
1050                 new_size = Pool_Size + CONNECTION_POOL;
1051                 
1052                 /* Im bisherigen Pool wurde keine freie Connection-Struktur mehr gefunden.
1053                  * Wenn erlaubt und moeglich muss nun der Pool vergroessert werden: */
1054                 
1055                 if( Conf_MaxConnections > 0 )
1056                 {
1057                         /* Es ist ein Limit konfiguriert */
1058                         if( Pool_Size >= Conf_MaxConnections )
1059                         {
1060                                 /* Mehr Verbindungen duerfen wir leider nicht mehr annehmen ... */
1061                                 Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", Pool_Size );
1062                                 close( new_sock );
1063                                 return;
1064                         }
1065                         if( new_size > Conf_MaxConnections ) new_size = Conf_MaxConnections;
1066                 }
1067                 if( new_size < Pool_Size )
1068                 {
1069                         Log( LOG_ALERT, "Can't accespt connection: limit (%d) reached -- overflow!", Pool_Size );
1070                         close( new_sock );
1071                         return;
1072                 }
1073                 
1074                 /* zunaechst realloc() versuchen; wenn das scheitert, malloc() versuchen
1075                  * und Daten ggf. "haendisch" umkopieren. (Haesslich! Eine wirklich
1076                  * dynamische Verwaltung waere wohl _deutlich_ besser ...) */
1077                 ptr = realloc( My_Connections, sizeof( CONNECTION ) * new_size );
1078                 if( ! ptr )
1079                 {
1080                         /* realloc() ist fehlgeschlagen. Nun malloc() probieren: */
1081                         ptr = malloc( sizeof( CONNECTION ) * new_size );
1082                         if( ! ptr )
1083                         {
1084                                 /* Offenbar steht kein weiterer Sepeicher zur Verfuegung :-( */
1085                                 Log( LOG_EMERG, "Can't allocate memory! [New_Connection]" );
1086                                 close( new_sock );
1087                                 return;
1088                         }
1089                         
1090                         /* Struktur umkopieren ... */
1091                         memcpy( ptr, My_Connections, sizeof( CONNECTION ) * Pool_Size );
1092                         
1093                         Log( LOG_DEBUG, "Allocated new connection pool for %ld items (%ld bytes). [malloc()/memcpy()]", new_size, sizeof( CONNECTION ) * new_size );
1094                 }
1095                 else Log( LOG_DEBUG, "Allocated new connection pool for %ld items (%ld bytes). [realloc()]", new_size, sizeof( CONNECTION ) * new_size );
1096                 
1097                 /* Adjust pointer to new block */
1098                 My_Connections = ptr;
1099                 
1100                 /* Initialize new items */
1101                 for( idx = Pool_Size; idx < new_size; idx++ ) Init_Conn_Struct( idx );
1102                 idx = Pool_Size;
1103                 
1104                 /* Adjust new pool size */
1105                 Pool_Size = new_size;
1106         }
1107
1108         /* Client-Struktur initialisieren */
1109         c = Client_NewLocal( idx, inet_ntoa( new_addr.sin_addr ), CLIENT_UNKNOWN, FALSE );
1110         if( ! c )
1111         {
1112                 Log( LOG_ALERT, "Can't accept connection: can't create client structure!" );
1113                 close( new_sock );
1114                 return;
1115         }
1116
1117         /* Verbindung registrieren */
1118         Init_Conn_Struct( idx );
1119         My_Connections[idx].sock = new_sock;
1120         My_Connections[idx].addr = new_addr;
1121
1122         /* Neuen Socket registrieren */
1123         FD_SET( new_sock, &My_Sockets );
1124         if( new_sock > Conn_MaxFD ) Conn_MaxFD = new_sock;
1125
1126         Log( LOG_INFO, "Accepted connection %d from %s:%d on socket %d.", idx, inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port), Sock );
1127
1128         /* Hostnamen ermitteln */
1129         strlcpy( My_Connections[idx].host, inet_ntoa( new_addr.sin_addr ), sizeof( My_Connections[idx].host ));
1130         Client_SetHostname( c, My_Connections[idx].host );
1131         s = Resolve_Addr( &new_addr );
1132         if( s )
1133         {
1134                 /* Sub-Prozess wurde asyncron gestartet */
1135                 My_Connections[idx].res_stat = s;
1136         }
1137         
1138         /* Penalty-Zeit setzen */
1139         Conn_SetPenalty( idx, 4 );
1140 } /* New_Connection */
1141
1142
1143 LOCAL CONN_ID
1144 Socket2Index( INT Sock )
1145 {
1146         /* zum Socket passende Connection suchen */
1147
1148         CONN_ID idx;
1149
1150         assert( Sock > NONE );
1151
1152         for( idx = 0; idx < Pool_Size; idx++ ) if( My_Connections[idx].sock == Sock ) break;
1153
1154         if( idx >= Pool_Size )
1155         {
1156                 /* die Connection wurde vermutlich (wegen eines
1157                  * Fehlers) bereits wieder abgebaut ... */
1158                 Log( LOG_DEBUG, "Socket2Index: can't get connection for socket %d!", Sock );
1159                 return NONE;
1160         }
1161         else return idx;
1162 } /* Socket2Index */
1163
1164
1165 LOCAL VOID
1166 Read_Request( CONN_ID Idx )
1167 {
1168         /* Daten von Socket einlesen und entsprechend behandeln.
1169          * Tritt ein Fehler auf, so wird der Socket geschlossen. */
1170
1171         INT len, bsize;
1172 #ifdef USE_ZLIB
1173         CLIENT *c;
1174 #endif
1175
1176         assert( Idx > NONE );
1177         assert( My_Connections[Idx].sock > NONE );
1178
1179         /* wenn noch nicht registriert: maximal mit ZREADBUFFER_LEN arbeiten,
1180          * ansonsten koennen Daten ggf. nicht umkopiert werden. */
1181         bsize = READBUFFER_LEN;
1182 #ifdef USE_ZLIB
1183         c = Client_GetFromConn( Idx );
1184         if(( Client_Type( c ) != CLIENT_USER ) && ( Client_Type( c ) != CLIENT_SERVER ) && ( Client_Type( c ) != CLIENT_SERVICE ) && ( bsize > ZREADBUFFER_LEN )) bsize = ZREADBUFFER_LEN;
1185 #endif
1186
1187 #ifdef USE_ZLIB
1188         if(( bsize - My_Connections[Idx].rdatalen - 1 < 1 ) || ( ZREADBUFFER_LEN - My_Connections[Idx].zip.rdatalen < 1 ))
1189 #else
1190         if( bsize - My_Connections[Idx].rdatalen - 1 < 1 )
1191 #endif
1192         {
1193                 /* Der Lesepuffer ist voll */
1194                 Log( LOG_ERR, "Read buffer overflow (connection %d): %d bytes!", Idx, My_Connections[Idx].rdatalen );
1195                 Conn_Close( Idx, "Read buffer overflow!", NULL, FALSE );
1196                 return;
1197         }
1198
1199 #ifdef USE_ZLIB
1200         if( My_Connections[Idx].options & CONN_ZIP )
1201         {
1202                 len = recv( My_Connections[Idx].sock, My_Connections[Idx].zip.rbuf + My_Connections[Idx].zip.rdatalen, ( ZREADBUFFER_LEN - My_Connections[Idx].zip.rdatalen ), 0 );
1203                 if( len > 0 ) My_Connections[Idx].zip.rdatalen += len;
1204         }
1205         else
1206 #endif
1207         {
1208                 len = recv( My_Connections[Idx].sock, My_Connections[Idx].rbuf + My_Connections[Idx].rdatalen, bsize - My_Connections[Idx].rdatalen - 1, 0 );
1209                 if( len > 0 ) My_Connections[Idx].rdatalen += len;
1210         }
1211
1212         if( len == 0 )
1213         {
1214                 /* Socket wurde geschlossen */
1215                 Log( LOG_INFO, "%s:%d (%s) is closing the connection ...", My_Connections[Idx].host, ntohs( My_Connections[Idx].addr.sin_port), inet_ntoa( My_Connections[Idx].addr.sin_addr ));
1216                 Conn_Close( Idx, "Socket closed!", "Client closed connection", FALSE );
1217                 return;
1218         }
1219
1220         if( len < 0 )
1221         {
1222                 /* Operation haette Socket "nur" blockiert ... */
1223                 if( errno == EAGAIN ) return;
1224
1225                 /* Fehler beim Lesen */
1226                 Log( LOG_ERR, "Read error on connection %d (socket %d): %s!", Idx, My_Connections[Idx].sock, strerror( errno ));
1227                 Conn_Close( Idx, "Read error!", "Client closed connection", FALSE );
1228                 return;
1229         }
1230
1231         /* Connection-Statistik aktualisieren */
1232         My_Connections[Idx].bytes_in += len;
1233
1234         /* Timestamp aktualisieren */
1235         My_Connections[Idx].lastdata = time( NULL );
1236
1237         Handle_Buffer( Idx );
1238 } /* Read_Request */
1239
1240
1241 LOCAL BOOLEAN
1242 Handle_Buffer( CONN_ID Idx )
1243 {
1244         /* Daten im Lese-Puffer einer Verbindung verarbeiten.
1245          * Wurde ein Request verarbeitet, so wird TRUE geliefert,
1246          * ansonsten FALSE (auch bei Fehlern). */
1247
1248 #ifndef STRICT_RFC
1249         CHAR *ptr1, *ptr2;
1250 #endif
1251         CHAR *ptr;
1252         INT len, delta;
1253         BOOLEAN action, result;
1254 #ifdef USE_ZLIB
1255         BOOLEAN old_z;
1256 #endif
1257
1258         result = FALSE;
1259         do
1260         {
1261 #ifdef USE_ZLIB
1262                 /* ggf. noch unkomprimiete Daten weiter entpacken */
1263                 if( My_Connections[Idx].options & CONN_ZIP )
1264                 {
1265                         if( ! Unzip_Buffer( Idx )) return FALSE;
1266                 }
1267 #endif
1268         
1269                 if( My_Connections[Idx].rdatalen < 1 ) break;
1270
1271                 /* Eine komplette Anfrage muss mit CR+LF enden, vgl.
1272                  * RFC 2812. Haben wir eine? */
1273                 My_Connections[Idx].rbuf[My_Connections[Idx].rdatalen] = '\0';
1274                 ptr = strstr( My_Connections[Idx].rbuf, "\r\n" );
1275         
1276                 if( ptr ) delta = 2;
1277 #ifndef STRICT_RFC
1278                 else
1279                 {
1280                         /* Nicht RFC-konforme Anfrage mit nur CR oder LF? Leider
1281                          * machen soetwas viele Clients, u.a. "mIRC" :-( */
1282                         ptr1 = strchr( My_Connections[Idx].rbuf, '\r' );
1283                         ptr2 = strchr( My_Connections[Idx].rbuf, '\n' );
1284                         delta = 1;
1285                         if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1;
1286                         else if( ptr1 ) ptr = ptr1;
1287                         else if( ptr2 ) ptr = ptr2;
1288                 }
1289 #endif
1290         
1291                 action = FALSE;
1292                 if( ptr )
1293                 {
1294                         /* Ende der Anfrage wurde gefunden */
1295                         *ptr = '\0';
1296                         len = ( ptr - My_Connections[Idx].rbuf ) + delta;
1297                         if( len > ( COMMAND_LEN - 1 ))
1298                         {
1299                                 /* Eine Anfrage darf(!) nicht laenger als 512 Zeichen
1300                                  * (incl. CR+LF!) werden; vgl. RFC 2812. Wenn soetwas
1301                                  * empfangen wird, wird der Client disconnectiert. */
1302                                 Log( LOG_ERR, "Request too long (connection %d): %d bytes (max. %d expected)!", Idx, My_Connections[Idx].rdatalen, COMMAND_LEN - 1 );
1303                                 Conn_Close( Idx, NULL, "Request too long", TRUE );
1304                                 return FALSE;
1305                         }
1306
1307 #ifdef USE_ZLIB
1308                         /* merken, ob Stream bereits komprimiert wird */
1309                         old_z = My_Connections[Idx].options & CONN_ZIP;
1310 #endif
1311
1312                         if( len > delta )
1313                         {
1314                                 /* Es wurde ein Request gelesen */
1315                                 My_Connections[Idx].msg_in++;
1316                                 if( ! Parse_Request( Idx, My_Connections[Idx].rbuf )) return FALSE;
1317                                 else action = TRUE;
1318                         }
1319
1320                         /* Puffer anpassen */
1321                         My_Connections[Idx].rdatalen -= len;
1322                         memmove( My_Connections[Idx].rbuf, My_Connections[Idx].rbuf + len, My_Connections[Idx].rdatalen );
1323
1324 #ifdef USE_ZLIB
1325                         if(( ! old_z ) && ( My_Connections[Idx].options & CONN_ZIP ) && ( My_Connections[Idx].rdatalen > 0 ))
1326                         {
1327                                 /* Mit dem letzten Befehl wurde Socket-Kompression aktiviert.
1328                                  * Evtl. schon vom Socket gelesene Daten in den Unzip-Puffer
1329                                  * umkopieren, damit diese nun zunaechst entkomprimiert werden */
1330                                 {
1331                                         if( My_Connections[Idx].rdatalen > ZREADBUFFER_LEN )
1332                                         {
1333                                                 /* Hupsa! Soviel Platz haben wir aber gar nicht! */
1334                                                 Log( LOG_ALERT, "Can't move read buffer: No space left in unzip buffer (need %d bytes)!", My_Connections[Idx].rdatalen );
1335                                                 return FALSE;
1336                                         }
1337                                         memcpy( My_Connections[Idx].zip.rbuf, My_Connections[Idx].rbuf, My_Connections[Idx].rdatalen );
1338                                         My_Connections[Idx].zip.rdatalen = My_Connections[Idx].rdatalen;
1339                                         My_Connections[Idx].rdatalen = 0;
1340                                         Log( LOG_DEBUG, "Moved already received data (%d bytes) to uncompression buffer.", My_Connections[Idx].zip.rdatalen );
1341                                 }
1342                         }
1343 #endif
1344                 }
1345                 
1346                 if( action ) result = TRUE;
1347         } while( action );
1348         
1349         return result;
1350 } /* Handle_Buffer */
1351
1352
1353 LOCAL VOID
1354 Check_Connections( VOID )
1355 {
1356         /* Pruefen, ob Verbindungen noch "alive" sind. Ist dies
1357          * nicht der Fall, zunaechst PING-PONG spielen und, wenn
1358          * auch das nicht "hilft", Client disconnectieren. */
1359
1360         CLIENT *c;
1361         CONN_ID i;
1362
1363         for( i = 0; i < Pool_Size; i++ )
1364         {
1365                 if( My_Connections[i].sock == NONE ) continue;
1366
1367                 c = Client_GetFromConn( i );
1368                 if( c && (( Client_Type( c ) == CLIENT_USER ) || ( Client_Type( c ) == CLIENT_SERVER ) || ( Client_Type( c ) == CLIENT_SERVICE )))
1369                 {
1370                         /* verbundener User, Server oder Service */
1371                         if( My_Connections[i].lastping > My_Connections[i].lastdata )
1372                         {
1373                                 /* es wurde bereits ein PING gesendet */
1374                                 if( My_Connections[i].lastping < time( NULL ) - Conf_PongTimeout )
1375                                 {
1376                                         /* Timeout */
1377                                         Log( LOG_DEBUG, "Connection %d: Ping timeout: %d seconds.", i, Conf_PongTimeout );
1378                                         Conn_Close( i, NULL, "Ping timeout", TRUE );
1379                                 }
1380                         }
1381                         else if( My_Connections[i].lastdata < time( NULL ) - Conf_PingTimeout )
1382                         {
1383                                 /* es muss ein PING gesendet werden */
1384                                 Log( LOG_DEBUG, "Connection %d: sending PING ...", i );
1385                                 My_Connections[i].lastping = time( NULL );
1386                                 Conn_WriteStr( i, "PING :%s", Client_ID( Client_ThisServer( )));
1387                         }
1388                 }
1389                 else
1390                 {
1391                         /* noch nicht vollstaendig aufgebaute Verbindung */
1392                         if( My_Connections[i].lastdata < time( NULL ) - Conf_PingTimeout )
1393                         {
1394                                 /* Timeout */
1395                                 Log( LOG_DEBUG, "Connection %d timed out ...", i );
1396                                 Conn_Close( i, NULL, "Timeout", FALSE );
1397                         }
1398                 }
1399         }
1400 } /* Check_Connections */
1401
1402
1403 LOCAL VOID
1404 Check_Servers( VOID )
1405 {
1406         /* Check if we can establish further server links */
1407
1408         RES_STAT *s;
1409         CONN_ID idx;
1410         INT i, n;
1411
1412         /* Don't connect in "passive mode" */
1413         if( NGIRCd_Passive ) return;
1414
1415         /* Serach all connections, are there results from the resolver? */
1416         for( idx = 0; idx < Pool_Size; idx++ )
1417         {
1418                 if( My_Connections[idx].sock != SERVER_WAIT ) continue;
1419
1420                 /* IP resolved? */
1421                 if( My_Connections[idx].res_stat == NULL ) New_Server( Conf_GetServer( idx ), idx );
1422         }
1423
1424         /* Check all configured servers */
1425         for( i = 0; i < MAX_SERVERS; i++ )
1426         {
1427                 /* Valid outgoing server which isn't already connected? */
1428                 if(( ! Conf_Server[i].host[0] ) || ( ! Conf_Server[i].port > 0 ) || ( Conf_Server[i].conn_id > NONE )) continue;
1429
1430                 /* Is there already a connection in this group? */
1431                 if( Conf_Server[i].group > NONE )
1432                 {
1433                         for( n = 0; n < MAX_SERVERS; n++ )
1434                         {
1435                                 if( n == i ) continue;
1436                                 if(( Conf_Server[n].conn_id > NONE ) && ( Conf_Server[n].group == Conf_Server[i].group )) break;
1437                         }
1438                         if( n < MAX_SERVERS ) continue;
1439                 }
1440
1441                 /* Check last connect attempt? */
1442                 if( Conf_Server[i].lasttry > time( NULL ) - Conf_ConnectRetry ) continue;
1443
1444                 /* Okay, try to connect now */
1445                 Conf_Server[i].lasttry = time( NULL );
1446
1447                 /* Search free connection structure */
1448                 for( idx = 0; idx < Pool_Size; idx++ ) if( My_Connections[idx].sock == NONE ) break;
1449                 if( idx >= Pool_Size )
1450                 {
1451                         Log( LOG_ALERT, "Can't establist server connection: connection limit reached (%d)!", Pool_Size );
1452                         return;
1453                 }
1454                 Log( LOG_DEBUG, "Preparing connection %d for \"%s\" ...", idx, Conf_Server[i].host );
1455
1456                 /* Verbindungs-Struktur initialisieren */
1457                 Init_Conn_Struct( idx );
1458                 My_Connections[idx].sock = SERVER_WAIT;
1459                 Conf_Server[i].conn_id = idx;
1460
1461                 /* Resolve Hostname. If this fails, try to use it as an IP address */
1462                 strlcpy( Conf_Server[i].ip, Conf_Server[i].host, sizeof( Conf_Server[i].ip ));
1463                 strlcpy( My_Connections[idx].host, Conf_Server[i].host, sizeof( My_Connections[idx].host ));
1464                 s = Resolve_Name( Conf_Server[i].host );
1465                 if( s )
1466                 {
1467                         /* Sub-Prozess wurde asyncron gestartet */
1468                         My_Connections[idx].res_stat = s;
1469                 }
1470         }
1471 } /* Check_Servers */
1472
1473
1474 LOCAL VOID
1475 New_Server( INT Server, CONN_ID Idx )
1476 {
1477         /* Neue Server-Verbindung aufbauen */
1478
1479         struct sockaddr_in new_addr;
1480         struct in_addr inaddr;
1481         INT res, new_sock;
1482         CLIENT *c;
1483
1484         assert( Server > NONE );
1485         assert( Idx > NONE );
1486
1487         /* Wurde eine gueltige IP-Adresse gefunden? */
1488         if( ! Conf_Server[Server].ip[0] )
1489         {
1490                 /* Nein. Verbindung wieder freigeben: */
1491                 Init_Conn_Struct( Idx );
1492                 Log( LOG_ERR, "Can't connect to \"%s\" (connection %d): ip address unknown!", Conf_Server[Server].host, Idx );
1493                 return;
1494         }
1495
1496         Log( LOG_INFO, "Establishing connection to \"%s\", %s, port %d (connection %d) ... ", Conf_Server[Server].host, Conf_Server[Server].ip, Conf_Server[Server].port, Idx );
1497
1498 #ifdef HAVE_INET_ATON
1499         if( inet_aton( Conf_Server[Server].ip, &inaddr ) == 0 )
1500 #else
1501         memset( &inaddr, 0, sizeof( inaddr ));
1502         inaddr.s_addr = inet_addr( Conf_Server[Server].ip );
1503         if( inaddr.s_addr == (unsigned)-1 )
1504 #endif
1505         {
1506                 /* Konnte Adresse nicht konvertieren */
1507                 Init_Conn_Struct( Idx );
1508                 Log( LOG_ERR, "Can't connect to \"%s\" (connection %d): can't convert ip address %s!", Conf_Server[Server].host, Idx, Conf_Server[Server].ip );
1509                 return;
1510         }
1511
1512         memset( &new_addr, 0, sizeof( new_addr ));
1513         new_addr.sin_family = AF_INET;
1514         new_addr.sin_addr = inaddr;
1515         new_addr.sin_port = htons( Conf_Server[Server].port );
1516
1517         new_sock = socket( PF_INET, SOCK_STREAM, 0 );
1518         if ( new_sock < 0 )
1519         {
1520                 Init_Conn_Struct( Idx );
1521                 Log( LOG_CRIT, "Can't create socket: %s!", strerror( errno ));
1522                 return;
1523         }
1524
1525         if( ! Init_Socket( new_sock )) return;
1526
1527         res = connect( new_sock, (struct sockaddr *)&new_addr, sizeof( new_addr ));
1528         if(( res != 0 ) && ( errno != EINPROGRESS ))
1529         {
1530                 Log( LOG_CRIT, "Can't connect socket: %s!", strerror( errno ));
1531                 close( new_sock );
1532                 Init_Conn_Struct( Idx );
1533                 return;
1534         }
1535
1536         /* Client-Struktur initialisieren */
1537         c = Client_NewLocal( Idx, inet_ntoa( new_addr.sin_addr ), CLIENT_UNKNOWNSERVER, FALSE );
1538         if( ! c )
1539         {
1540                 close( new_sock );
1541                 Init_Conn_Struct( Idx );
1542                 Log( LOG_ALERT, "Can't establish connection: can't create client structure!" );
1543                 return;
1544         }
1545         Client_SetIntroducer( c, c );
1546         Client_SetToken( c, TOKEN_OUTBOUND );
1547
1548         /* Verbindung registrieren */
1549         My_Connections[Idx].sock = new_sock;
1550         My_Connections[Idx].addr = new_addr;
1551         strlcpy( My_Connections[Idx].host, Conf_Server[Server].host, sizeof( My_Connections[Idx].host ));
1552
1553         /* Neuen Socket registrieren */
1554         FD_SET( new_sock, &My_Sockets );
1555         FD_SET( new_sock, &My_Connects );
1556         if( new_sock > Conn_MaxFD ) Conn_MaxFD = new_sock;
1557         
1558         Log( LOG_DEBUG, "Registered new connection %d on socket %d.", Idx, My_Connections[Idx].sock );
1559 } /* New_Server */
1560
1561
1562 LOCAL VOID
1563 Init_Conn_Struct( CONN_ID Idx )
1564 {
1565         /* Connection-Struktur initialisieren */
1566
1567         My_Connections[Idx].sock = NONE;
1568         My_Connections[Idx].res_stat = NULL;
1569         My_Connections[Idx].host[0] = '\0';
1570         My_Connections[Idx].rbuf[0] = '\0';
1571         My_Connections[Idx].rdatalen = 0;
1572         My_Connections[Idx].wbuf[0] = '\0';
1573         My_Connections[Idx].wdatalen = 0;
1574         My_Connections[Idx].starttime = time( NULL );
1575         My_Connections[Idx].lastdata = time( NULL );
1576         My_Connections[Idx].lastping = 0;
1577         My_Connections[Idx].lastprivmsg = time( NULL );
1578         My_Connections[Idx].delaytime = 0;
1579         My_Connections[Idx].bytes_in = 0;
1580         My_Connections[Idx].bytes_out = 0;
1581         My_Connections[Idx].msg_in = 0;
1582         My_Connections[Idx].msg_out = 0;
1583         My_Connections[Idx].flag = 0;
1584         My_Connections[Idx].options = 0;
1585
1586 #ifdef USE_ZLIB
1587         My_Connections[Idx].zip.rbuf[0] = '\0';
1588         My_Connections[Idx].zip.rdatalen = 0;
1589         My_Connections[Idx].zip.wbuf[0] = '\0';
1590         My_Connections[Idx].zip.wdatalen = 0;
1591         My_Connections[Idx].zip.bytes_in = 0;
1592         My_Connections[Idx].zip.bytes_out = 0;
1593 #endif
1594 } /* Init_Conn_Struct */
1595
1596
1597 LOCAL BOOLEAN
1598 Init_Socket( INT Sock )
1599 {
1600         /* Socket-Optionen setzen */
1601
1602         INT on = 1;
1603
1604 #ifdef O_NONBLOCK       /* A/UX kennt das nicht? */
1605         if( fcntl( Sock, F_SETFL, O_NONBLOCK ) != 0 )
1606         {
1607                 Log( LOG_CRIT, "Can't enable non-blocking mode: %s!", strerror( errno ));
1608                 close( Sock );
1609                 return FALSE;
1610         }
1611 #endif
1612         if( setsockopt( Sock, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof( on )) != 0)
1613         {
1614                 Log( LOG_ERR, "Can't set socket options: %s!", strerror( errno ));
1615                 /* dieser Fehler kann ignoriert werden. */
1616         }
1617
1618         return TRUE;
1619 } /* Init_Socket */
1620
1621
1622 LOCAL VOID
1623 Read_Resolver_Result( INT r_fd )
1624 {
1625         /* Ergebnis von Resolver Sub-Prozess aus Pipe lesen
1626          * und entsprechende Connection aktualisieren */
1627
1628         CHAR result[HOST_LEN];
1629         CLIENT *c;
1630         INT len, i, n;
1631
1632         FD_CLR( r_fd, &Resolver_FDs );
1633
1634         /* Anfrage vom Parent lesen */
1635         len = read( r_fd, result, HOST_LEN - 1 );
1636         if( len < 0 )
1637         {
1638                 /* Fehler beim Lesen aus der Pipe */
1639                 close( r_fd );
1640                 Log( LOG_CRIT, "Resolver: Can't read result: %s!", strerror( errno ));
1641                 return;
1642         }
1643         result[len] = '\0';
1644
1645         /* zugehoerige Connection suchen */
1646         for( i = 0; i < Pool_Size; i++ )
1647         {
1648                 if(( My_Connections[i].sock != NONE ) && ( My_Connections[i].res_stat ) && ( My_Connections[i].res_stat->pipe[0] == r_fd )) break;
1649         }
1650         if( i >= Pool_Size )
1651         {
1652                 /* Opsa! Keine passende Connection gefunden!? Vermutlich
1653                  * wurde sie schon wieder geschlossen. */
1654                 close( r_fd );
1655                 Log( LOG_DEBUG, "Resolver: Got result for unknown connection!?" );
1656                 return;
1657         }
1658
1659         Log( LOG_DEBUG, "Resolver: %s is \"%s\".", My_Connections[i].host, result );
1660         
1661         /* Aufraeumen */
1662         close( My_Connections[i].res_stat->pipe[0] );
1663         close( My_Connections[i].res_stat->pipe[1] );
1664         free( My_Connections[i].res_stat );
1665         My_Connections[i].res_stat = NULL;
1666
1667         if( My_Connections[i].sock > NONE )
1668         {
1669                 /* Eingehende Verbindung: Hostnamen setzen */
1670                 c = Client_GetFromConn( i );
1671                 assert( c != NULL );
1672                 strlcpy( My_Connections[i].host, result, sizeof( My_Connections[i].host ));
1673                 Client_SetHostname( c, result );
1674         }
1675         else
1676         {
1677                 /* Ausgehende Verbindung (=Server): IP setzen */
1678                 n = Conf_GetServer( i );
1679                 if( n > NONE ) strlcpy( Conf_Server[n].ip, result, sizeof( Conf_Server[n].ip ));
1680                 else Log( LOG_ERR, "Got resolver result for non-configured server!?" );
1681         }
1682
1683         /* Penalty-Zeit zurueck setzen */
1684         Conn_ResetPenalty( i );
1685 } /* Read_Resolver_Result */
1686
1687
1688 /* -eof- */