]> arthur.barton.de Git - ngircd-alex.git/blob - src/ngircd/conn.c
- Asyncroner Resolver Hostname->IP.
[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  * Dieses Programm ist freie Software. Sie koennen es unter den Bedingungen
6  * der GNU General Public License (GPL), wie von der Free Software Foundation
7  * herausgegeben, weitergeben und/oder modifizieren, entweder unter Version 2
8  * der Lizenz oder (wenn Sie es wuenschen) jeder spaeteren Version.
9  * Naehere Informationen entnehmen Sie bitter der Datei COPYING. Eine Liste
10  * der an ngIRCd beteiligten Autoren finden Sie in der Datei AUTHORS.
11  *
12  * $Id: conn.c,v 1.26 2002/01/02 02:50:47 alex Exp $
13  *
14  * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
15  *
16  * $Log: conn.c,v $
17  * Revision 1.26  2002/01/02 02:50:47  alex
18  * - Asyncroner Resolver Hostname->IP.
19  * - Server-Links begonnen zu implementieren. Die Verbindung wird aufgebaut,
20  *   jedoch noch keine SERVER-Befehle verschickt.
21  * - Diverse Bug-Fixes und kleinere Erweiterungen.
22  *
23  * Revision 1.24  2002/01/01 18:25:44  alex
24  * - #include's fuer stdlib.h ergaenzt.
25  *
26  * Revision 1.23  2001/12/31 02:18:51  alex
27  * - viele neue Befehle (WHOIS, ISON, OPER, DIE, RESTART),
28  * - neuen Header "defines.h" mit (fast) allen Konstanten.
29  * - Code Cleanups und viele "kleine" Aenderungen & Bugfixes.
30  *
31  * Revision 1.22  2001/12/30 19:26:11  alex
32  * - Unterstuetzung fuer die Konfigurationsdatei eingebaut.
33  *
34  * Revision 1.21  2001/12/29 22:33:36  alex
35  * - bessere Dokumentation des Modules bzw. der Funktionen.
36  *
37  * Revision 1.20  2001/12/29 22:09:43  alex
38  * - kleinere Aenderungen ("clean-ups") bei Logging (Resolver).
39  *
40  * Revision 1.19  2001/12/29 21:53:57  alex
41  * - Da hatte ich mich wohl ein wenig verrannt; jetzt sollte der Resolver
42  *   aber tatsaechlich funktionieren.
43  *
44  * Revision 1.18  2001/12/29 20:17:25  alex
45  * - asyncronen Resolver (IP->Name) implementiert, dadurch div. Aenderungen.
46  *
47  * Revision 1.17  2001/12/29 03:06:16  alex
48  * - Loglevel (nochmal) angepasst.
49  *
50  * Revision 1.16  2001/12/27 19:32:44  alex
51  * - bei "Null-Requests" wird nichts mehr geloggt. Uberfluessig, da normal.
52  *
53  * Revision 1.15  2001/12/27 16:35:04  alex
54  * - vergessene Variable bei Ping-Timeout-Logmeldung ergaenzt. Opsa.
55  *
56  * Revision 1.14  2001/12/26 14:45:37  alex
57  * - "Code Cleanups".
58  *
59  * Revision 1.13  2001/12/26 03:36:57  alex
60  * - Verbindungen mit Lesefehlern werden nun korrekt terminiert.
61  *
62  * Revision 1.12  2001/12/26 03:20:53  alex
63  * - PING/PONG-Timeout implementiert.
64  *
65  * Revision 1.11  2001/12/25 23:15:16  alex
66  * - buffer werden nun periodisch geprueft, keine haengenden Clients mehr.
67  *
68  * Revision 1.10  2001/12/25 22:03:47  alex
69  * - Conn_Close() eingefuehrt: war die lokale Funktion Close_Connection().
70  *
71  * Revision 1.9  2001/12/24 01:32:33  alex
72  * - in Conn_WriteStr() wurde das CR+LF nicht angehaengt!
73  * - Fehler-Ausgaben vereinheitlicht.
74  *
75  * Revision 1.8  2001/12/23 22:02:54  alex
76  * - Conn_WriteStr() nimmt nun variable Parameter,
77  * - diverse kleinere Aenderungen.
78  *
79  * Revision 1.7  2001/12/21 22:24:25  alex
80  * - kleinere Aenderungen an den Log-Meldungen,
81  * - Parse_Request() wird aufgerufen.
82  *
83  * Revision 1.6  2001/12/15 00:11:55  alex
84  * - Lese- und Schreib-Puffer implementiert.
85  * - einige neue (Unter-)Funktionen eingefuehrt.
86  * - diverse weitere kleinere Aenderungen.
87  *
88  * Revision 1.5  2001/12/14 08:16:47  alex
89  * - Begonnen, Client-spezifische Lesepuffer zu implementieren.
90  * - Umstellung auf Datentyp "CONN_ID".
91  *
92  * Revision 1.4  2001/12/13 02:04:16  alex
93  * - boesen "Speicherschiesser" in Log() gefixt.
94  *
95  * Revision 1.3  2001/12/13 01:33:09  alex
96  * - Conn_Handler() unterstuetzt nun einen Timeout.
97  * - fuer Verbindungen werden keine FILE-Handles mehr benutzt.
98  * - kleinere "Code Cleanups" ;-)
99  *
100  * Revision 1.2  2001/12/12 23:32:02  alex
101  * - diverse Erweiterungen und Verbesserungen (u.a. sind nun mehrere
102  *   Verbindungen und Listen-Sockets moeglich).
103  *
104  * Revision 1.1  2001/12/12 17:18:38  alex
105  * - Modul zur Verwaltung aller Netzwerk-Verbindungen begonnen.
106  */
107
108
109 #include <portab.h>
110 #include "global.h"
111
112 #include <imp.h>
113 #include <assert.h>
114 #include <stdarg.h>
115 #include <stdio.h>
116 #include <stdlib.h>
117 #include <unistd.h>
118 #include <errno.h>
119 #include <fcntl.h>
120 #include <string.h>
121 #include <sys/socket.h>
122 #include <sys/time.h>
123 #include <sys/types.h>
124 #include <time.h>
125 #include <netinet/in.h>
126 #include <arpa/inet.h>
127 #include <netdb.h>
128
129 #ifdef HAVE_STDINT_H
130 #include <stdint.h>                     /* u.a. fuer Mac OS X */
131 #endif
132
133 #include "ngircd.h"
134 #include "client.h"
135 #include "conf.h"
136 #include "log.h"
137 #include "parse.h"
138 #include "tool.h"
139
140 #include <exp.h>
141 #include "conn.h"
142
143
144 #define SERVER_WAIT NONE - 1
145
146
147 typedef struct _Connection
148 {
149         INT sock;                       /* Socket Handle */
150         struct sockaddr_in addr;        /* Adresse des Client */
151         RES_STAT *res_stat;             /* "Resolver-Status", s.o. */
152         CHAR host[HOST_LEN];            /* Hostname */
153         CHAR rbuf[READBUFFER_LEN];      /* Lesepuffer */
154         INT rdatalen;                   /* Laenge der Daten im Lesepuffer */
155         CHAR wbuf[WRITEBUFFER_LEN];     /* Schreibpuffer */
156         INT wdatalen;                   /* Laenge der Daten im Schreibpuffer */
157         INT our_server;                 /* wenn von uns zu connectender Server: ID */
158         time_t lastdata;                /* Letzte Aktivitaet */
159         time_t lastping;                /* Letzter PING */
160         time_t lastprivmsg;             /* Letzte PRIVMSG */
161 } CONNECTION;
162
163
164 LOCAL VOID Handle_Read( INT sock );
165 LOCAL BOOLEAN Handle_Write( CONN_ID Idx );
166 LOCAL VOID New_Connection( INT Sock );
167 LOCAL CONN_ID Socket2Index( INT Sock );
168 LOCAL VOID Read_Request( CONN_ID Idx );
169 LOCAL BOOLEAN Try_Write( CONN_ID Idx );
170 LOCAL VOID Handle_Buffer( CONN_ID Idx );
171 LOCAL VOID Check_Connections( VOID );
172 LOCAL VOID Check_Servers( VOID );
173 LOCAL VOID Init_Conn_Struct( INT Idx );
174 LOCAL VOID New_Server( INT Server, CONN_ID Idx );
175
176 LOCAL RES_STAT *ResolveAddr( struct sockaddr_in *Addr );
177 LOCAL RES_STAT *ResolveName( CHAR *Host );
178 LOCAL VOID Do_ResolveAddr( struct sockaddr_in *Addr, INT w_fd );
179 LOCAL VOID Do_ResolveName( CHAR *Host, INT w_fd );
180 LOCAL VOID Read_Resolver_Result( INT r_fd );
181 LOCAL CHAR *Resolv_Error( INT H_Error );
182
183
184 LOCAL fd_set My_Listeners;
185 LOCAL fd_set My_Sockets;
186 LOCAL fd_set My_Resolvers;
187
188 LOCAL INT My_Max_Fd;
189
190 LOCAL CONNECTION My_Connections[MAX_CONNECTIONS];
191
192
193 GLOBAL VOID Conn_Init( VOID )
194 {
195         /* Modul initialisieren: statische Strukturen "ausnullen". */
196
197         CONN_ID i;
198
199         /* zu Beginn haben wir keine Verbindungen */
200         FD_ZERO( &My_Listeners );
201         FD_ZERO( &My_Sockets );
202         FD_ZERO( &My_Resolvers );
203
204         My_Max_Fd = 0;
205
206         /* Connection-Struktur initialisieren */
207         for( i = 0; i < MAX_CONNECTIONS; i++ ) Init_Conn_Struct( i );
208 } /* Conn_Init */
209
210
211 GLOBAL VOID Conn_Exit( VOID )
212 {
213         /* Modul abmelden: alle noch offenen Connections
214          * schliessen und freigeben. */
215
216         CONN_ID idx;
217         INT i;
218
219         /* Sockets schliessen */
220         for( i = 0; i < My_Max_Fd + 1; i++ )
221         {
222                 if( FD_ISSET( i, &My_Sockets ))
223                 {
224                         for( idx = 0; idx < MAX_CONNECTIONS; idx++ )
225                         {
226                                 if( My_Connections[idx].sock == i ) break;
227                         }
228                         if( idx < MAX_CONNECTIONS ) Conn_Close( idx, "Server going down ..." );
229                         else if( FD_ISSET( i, &My_Listeners ))
230                         {
231                                 close( i );
232                                 Log( LOG_INFO, "Listening socket %d closed.", i );
233                         }
234                         else
235                         {
236                                 close( i );
237                                 Log( LOG_WARNING, "Unknown connection %d closed.", i );
238                         }
239                 }
240         }
241 } /* Conn_Exit */
242
243
244 GLOBAL BOOLEAN Conn_NewListener( CONST INT Port )
245 {
246         /* Neuen Listen-Socket erzeugen: der Server wartet dann auf
247          * dem angegebenen Port auf Verbindungen. Kann der Listen-
248          * Socket nicht erteugt werden, so wird NULL geliefert.*/
249
250         struct sockaddr_in addr;
251         INT sock, on = 1;
252
253         /* Server-"Listen"-Socket initialisieren */
254         memset( &addr, 0, sizeof( addr ));
255         addr.sin_family = AF_INET;
256         addr.sin_port = htons( Port );
257         addr.sin_addr.s_addr = htonl( INADDR_ANY );
258
259         /* Socket erzeugen */
260         sock = socket( PF_INET, SOCK_STREAM, 0);
261         if( sock < 0 )
262         {
263                 Log( LOG_ALERT, "Can't create socket: %s!", strerror( errno ));
264                 return FALSE;
265         }
266
267         /* Socket-Optionen setzen */
268         if( fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 )
269         {
270                 Log( LOG_ALERT, "Can't enable non-blocking mode: %s!", strerror( errno ));
271                 close( sock );
272                 return FALSE;
273         }
274         if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof( on )) != 0)
275         {
276                 Log( LOG_CRIT, "Can't set socket options: %s!", strerror( errno ));
277                 /* dieser Fehler kann ignoriert werden. */
278         }
279
280         /* an Port binden */
281         if( bind( sock, (struct sockaddr *)&addr, (socklen_t)sizeof( addr )) != 0 )
282         {
283                 Log( LOG_ALERT, "Can't bind socket: %s!", strerror( errno ));
284                 close( sock );
285                 return FALSE;
286         }
287
288         /* in "listen mode" gehen :-) */
289         if( listen( sock, 10 ) != 0 )
290         {
291                 Log( LOG_ALERT, "Can't listen on soecket: %s!", strerror( errno ));
292                 close( sock );
293                 return FALSE;
294         }
295
296         /* Neuen Listener in Strukturen einfuegen */
297         FD_SET( sock, &My_Listeners );
298         FD_SET( sock, &My_Sockets );
299
300         if( sock > My_Max_Fd ) My_Max_Fd = sock;
301
302         Log( LOG_INFO, "Now listening on port %d (socket %d).", Port, sock );
303
304         return TRUE;
305 } /* Conn_NewListener */
306
307
308 GLOBAL VOID Conn_Handler( INT Timeout )
309 {
310         /* Aktive Verbindungen ueberwachen. Mindestens alle "Timeout"
311          * Sekunden wird die Funktion verlassen. Folgende Aktionen
312          * werden durchgefuehrt:
313          *  - neue Verbindungen annehmen,
314          *  - Server-Verbindungen aufbauen,
315          *  - geschlossene Verbindungen loeschen,
316          *  - volle Schreibpuffer versuchen zu schreiben,
317          *  - volle Lesepuffer versuchen zu verarbeiten,
318          *  - Antworten von Resolver Sub-Prozessen annehmen.
319          */
320
321         fd_set read_sockets, write_sockets;
322         struct timeval tv;
323         time_t start;
324         INT i;
325
326         start = time( NULL );
327         while(( time( NULL ) - start < Timeout ) && ( ! NGIRCd_Quit ))
328         {
329                 Check_Servers( );
330
331                 Check_Connections( );
332
333                 /* Timeout initialisieren */
334                 tv.tv_sec = 0;
335                 tv.tv_usec = 50000;
336
337                 /* noch volle Lese-Buffer suchen */
338                 for( i = 0; i < MAX_CONNECTIONS; i++ )
339                 {
340                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].rdatalen > 0 ))
341                         {
342                                 /* Kann aus dem Buffer noch ein Befehl extrahiert werden? */
343                                 Handle_Buffer( i );
344                         }
345                 }
346                 
347                 /* noch volle Schreib-Puffer suchen */
348                 FD_ZERO( &write_sockets );
349                 for( i = 0; i < MAX_CONNECTIONS; i++ )
350                 {
351                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].wdatalen > 0 ))
352                         {
353                                 /* Socket der Verbindung in Set aufnehmen */
354                                 FD_SET( My_Connections[i].sock, &write_sockets );
355                         }
356                 }
357
358                 /* von welchen Sockets koennte gelesen werden? */
359                 read_sockets = My_Sockets;
360                 for( i = 0; i < MAX_CONNECTIONS; i++ )
361                 {
362                         if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].host[0] == '\0' ))
363                         {
364                                 /* Hier muss noch auf den Resolver Sub-Prozess gewartet werden */
365                                 FD_CLR( My_Connections[i].sock, &read_sockets );
366                         }
367                 }
368                 for( i = 0; i < My_Max_Fd + 1; i++ )
369                 {
370                         /* Pipes von Resolver Sub-Prozessen aufnehmen */
371                         if( FD_ISSET( i, &My_Resolvers ))
372                         {
373                                 FD_SET( i, &read_sockets );
374                         }
375                 }
376
377                 /* Auf Aktivitaet warten */
378                 if( select( My_Max_Fd + 1, &read_sockets, &write_sockets, NULL, &tv ) == -1 )
379                 {
380                         if( errno != EINTR ) Log( LOG_ALERT, "select(): %s!", strerror( errno ));
381                         return;
382                 }
383
384                 /* Koennen Daten geschrieben werden? */
385                 for( i = 0; i < My_Max_Fd + 1; i++ )
386                 {
387                         if( FD_ISSET( i, &write_sockets )) Handle_Write( Socket2Index( i ));
388                 }
389
390                 /* Daten zum Lesen vorhanden? */
391                 for( i = 0; i < My_Max_Fd + 1; i++ )
392                 {
393                         if( FD_ISSET( i, &read_sockets )) Handle_Read( i );
394                 }
395         }
396 } /* Conn_Handler */
397
398
399 GLOBAL BOOLEAN Conn_WriteStr( CONN_ID Idx, CHAR *Format, ... )
400 {
401         /* String in Socket schreiben. CR+LF wird von dieser Funktion
402          * automatisch angehaengt. Im Fehlerfall wird dir Verbindung
403          * getrennt und FALSE geliefert. */
404
405         CHAR buffer[COMMAND_LEN];
406         BOOLEAN ok;
407         va_list ap;
408
409         va_start( ap, Format );
410         if( vsnprintf( buffer, COMMAND_LEN - 2, Format, ap ) == COMMAND_LEN - 2 )
411         {
412                 Log( LOG_ALERT, "String too long to send (connection %d)!", Idx );
413                 Conn_Close( Idx, "Server error: String too long to send!" );
414                 return FALSE;
415         }
416
417 #ifdef SNIFFER
418         Log( LOG_DEBUG, " -> connection %d: '%s'.", Idx, buffer );
419 #endif
420
421         strcat( buffer, "\r\n" );
422         ok = Conn_Write( Idx, buffer, strlen( buffer ));
423
424         va_end( ap );
425         return ok;
426 } /* Conn_WriteStr */
427
428
429 GLOBAL BOOLEAN Conn_Write( CONN_ID Idx, CHAR *Data, INT Len )
430 {
431         /* Daten in Socket schreiben. Bei "fatalen" Fehlern wird
432          * der Client disconnectiert und FALSE geliefert. */
433
434         assert( Idx >= 0 );
435         assert( My_Connections[Idx].sock > NONE );
436         assert( Data != NULL );
437         assert( Len > 0 );
438
439         /* pruefen, ob Daten im Schreibpuffer sind. Wenn ja, zunaechst
440          * pruefen, ob diese gesendet werden koennen */
441         if( My_Connections[Idx].wdatalen > 0 )
442         {
443                 if( ! Try_Write( Idx )) return FALSE;
444         }
445
446         /* pruefen, ob im Schreibpuffer genuegend Platz ist */
447         if( WRITEBUFFER_LEN - My_Connections[Idx].wdatalen - Len <= 0 )
448         {
449                 /* der Puffer ist dummerweise voll ... */
450                 Log( LOG_NOTICE, "Write buffer overflow (connection %d)!", Idx );
451                 Conn_Close( Idx, NULL );
452                 return FALSE;
453         }
454
455         /* Daten in Puffer kopieren */
456         memcpy( My_Connections[Idx].wbuf + My_Connections[Idx].wdatalen, Data, Len );
457         My_Connections[Idx].wdatalen += Len;
458
459         /* pruefen, on Daten vorhanden sind und geschrieben werden koennen */
460         if( My_Connections[Idx].wdatalen > 0 )
461         {
462                 if( ! Try_Write( Idx )) return FALSE;
463         }
464
465         return TRUE;
466 } /* Conn_Write */
467
468
469 GLOBAL VOID Conn_Close( CONN_ID Idx, CHAR *Msg )
470 {
471         /* Verbindung schliessen. Evtl. noch von Resolver
472          * Sub-Prozessen offene Pipes werden geschlossen. */
473
474         CLIENT *c;
475         
476         assert( Idx >= 0 );
477         assert( My_Connections[Idx].sock > NONE );
478
479         if( Msg )
480         {
481                 Conn_WriteStr( Idx, "ERROR :%s", Msg );
482                 if( My_Connections[Idx].sock == NONE ) return;
483         }
484
485         if( close( My_Connections[Idx].sock ) != 0 )
486         {
487                 Log( LOG_ERR, "Error closing connection %d with %s:%d - %s!", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
488         }
489         else
490         {
491                 Log( LOG_NOTICE, "Connection %d with %s:%d closed.", Idx, inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
492         }
493
494         c = Client_GetFromConn( Idx );
495         if( c ) Client_Destroy( c );
496
497         if( My_Connections[Idx].res_stat )
498         {
499                 /* Resolver-Strukturen freigeben, wenn noch nicht geschehen */
500                 FD_CLR( My_Connections[Idx].res_stat->pipe[0], &My_Resolvers );
501                 close( My_Connections[Idx].res_stat->pipe[0] );
502                 close( My_Connections[Idx].res_stat->pipe[1] );
503                 free( My_Connections[Idx].res_stat );
504         }
505
506         /* Bei Server-Verbindungen lasttry-Zeitpunkt auf "jetzt" setzen */
507         if( My_Connections[Idx].our_server >= 0 ) Conf_Server[My_Connections[Idx].our_server].lasttry = time( NULL );
508
509         FD_CLR( My_Connections[Idx].sock, &My_Sockets );
510         My_Connections[Idx].sock = NONE;
511 } /* Conn_Close */
512
513
514 GLOBAL VOID Conn_UpdateIdle( CONN_ID Idx )
515 {
516         /* Idle-Timer zuruecksetzen */
517
518         assert( Idx >= 0 );
519         My_Connections[Idx].lastprivmsg = time( NULL );
520 }
521
522
523 GLOBAL INT32 Conn_GetIdle( CONN_ID Idx )
524 {
525         /* Idle-Time einer Verbindung liefern (in Sekunden) */
526
527         assert( Idx >= 0 );
528         return time( NULL ) - My_Connections[Idx].lastprivmsg;
529 } /* Conn_GetIdle */
530
531
532 LOCAL BOOLEAN Try_Write( CONN_ID Idx )
533 {
534         /* Versuchen, Daten aus dem Schreib-Puffer in den
535          * Socket zu schreiben. */
536
537         fd_set write_socket;
538
539         assert( Idx >= 0 );
540         assert( My_Connections[Idx].sock > NONE );
541         assert( My_Connections[Idx].wdatalen > 0 );
542
543         FD_ZERO( &write_socket );
544         FD_SET( My_Connections[Idx].sock, &write_socket );
545         if( select( My_Connections[Idx].sock + 1, NULL, &write_socket, NULL, 0 ) == -1 )
546         {
547                 /* Fehler! */
548                 if( errno != EINTR )
549                 {
550                         Log( LOG_ALERT, "select(): %s!", strerror( errno ));
551                         Conn_Close( Idx, NULL );
552                         return FALSE;
553                 }
554         }
555
556         if( FD_ISSET( My_Connections[Idx].sock, &write_socket )) return Handle_Write( Idx );
557         else return TRUE;
558 } /* Try_Write */
559
560
561 LOCAL VOID Handle_Read( INT Sock )
562 {
563         /* Aktivitaet auf einem Socket verarbeiten:
564          *  - neue Clients annehmen,
565          *  - Daten von Clients verarbeiten,
566          *  - Resolver-Rueckmeldungen annehmen. */
567
568         CONN_ID idx;
569
570         assert( Sock >= 0 );
571         
572         if( FD_ISSET( Sock, &My_Listeners ))
573         {
574                 /* es ist einer unserer Listener-Sockets: es soll
575                  * also eine neue Verbindung aufgebaut werden. */
576
577                 New_Connection( Sock );
578         }
579         else if( FD_ISSET( Sock, &My_Resolvers ))
580         {
581                 /* Rueckmeldung von einem Resolver Sub-Prozess */
582
583                 Read_Resolver_Result( Sock );
584         }
585         else
586         {
587                 /* Ein Client Socket: entweder ein User oder Server */
588
589                 idx = Socket2Index( Sock );
590                 Read_Request( idx );
591         }
592 } /* Handle_Read */
593
594
595 LOCAL BOOLEAN Handle_Write( CONN_ID Idx )
596 {
597         /* Daten aus Schreibpuffer versenden */
598
599         INT len;
600
601         assert( Idx >= 0 );
602         assert( My_Connections[Idx].sock > NONE );
603         assert( My_Connections[Idx].wdatalen > 0 );
604
605         /* Daten schreiben */
606         len = send( My_Connections[Idx].sock, My_Connections[Idx].wbuf, My_Connections[Idx].wdatalen, 0 );
607         if( len < 0 )
608         {
609                 /* Oops, ein Fehler! */
610                 Log( LOG_ALERT, "Write error (buffer) on connection %d: %s!", Idx, strerror( errno ));
611                 Conn_Close( Idx, NULL );
612                 return FALSE;
613         }
614
615         /* Puffer anpassen */
616         My_Connections[Idx].wdatalen -= len;
617         memmove( My_Connections[Idx].wbuf, My_Connections[Idx].wbuf + len, My_Connections[Idx].wdatalen );
618
619         return TRUE;
620 } /* Handle_Write */
621
622
623 LOCAL VOID New_Connection( INT Sock )
624 {
625         /* Neue Client-Verbindung von Listen-Socket annehmen und
626          * CLIENT-Struktur anlegen. */
627
628         struct sockaddr_in new_addr;
629         INT new_sock, new_sock_len;
630         RES_STAT *s;
631         CONN_ID idx;
632
633         assert( Sock >= 0 );
634
635         new_sock_len = sizeof( new_addr );
636         new_sock = accept( Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len );
637         if( new_sock < 0 )
638         {
639                 Log( LOG_CRIT, "Can't accept connection: %s!", strerror( errno ));
640                 return;
641         }
642
643         /* Freie Connection-Struktur suschen */
644         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == NONE ) break;
645         if( idx >= MAX_CONNECTIONS )
646         {
647                 Log( LOG_ALERT, "Can't accept connection: limit reached (%d)!", MAX_CONNECTIONS );
648                 close( new_sock );
649                 return;
650         }
651
652         /* Client-Struktur initialisieren */
653         if( ! Client_NewLocal( idx, inet_ntoa( new_addr.sin_addr )))
654         {
655                 Log( LOG_ALERT, "Can't accept connection: can't create client structure!" );
656                 close( new_sock );
657                 return;
658         }
659
660         /* Verbindung registrieren */
661         Init_Conn_Struct( idx );
662         My_Connections[idx].sock = new_sock;
663         My_Connections[idx].addr = new_addr;
664
665         /* Neuen Socket registrieren */
666         FD_SET( new_sock, &My_Sockets );
667         if( new_sock > My_Max_Fd ) My_Max_Fd = new_sock;
668
669         Log( LOG_NOTICE, "Accepted connection %d from %s:%d on socket %d.", idx, inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port), Sock );
670
671         /* Hostnamen ermitteln */
672         s = ResolveAddr( &new_addr );
673         if( s )
674         {
675                 /* Sub-Prozess wurde asyncron gestartet */
676                 My_Connections[idx].res_stat = s;
677         }
678         else
679         {
680                 /* kann Namen nicht aufloesen */
681                 strcpy( My_Connections[idx].host, inet_ntoa( new_addr.sin_addr ));
682         }
683 } /* New_Connection */
684
685
686 LOCAL CONN_ID Socket2Index( INT Sock )
687 {
688         /* zum Socket passende Connection suchen */
689
690         CONN_ID idx;
691
692         assert( Sock >= 0 );
693
694         for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == Sock ) break;
695
696         assert( idx < MAX_CONNECTIONS );
697         return idx;
698 } /* Socket2Index */
699
700
701 LOCAL VOID Read_Request( CONN_ID Idx )
702 {
703         /* Daten von Socket einlesen und entsprechend behandeln.
704          * Tritt ein Fehler auf, so wird der Socket geschlossen. */
705
706         INT len;
707
708         assert( Idx >= 0 );
709         assert( My_Connections[Idx].sock > NONE );
710
711         len = recv( My_Connections[Idx].sock, My_Connections[Idx].rbuf + My_Connections[Idx].rdatalen, READBUFFER_LEN - My_Connections[Idx].rdatalen - 1, 0 );
712         My_Connections[Idx].rbuf[READBUFFER_LEN - 1] = '\0';
713
714         if( len == 0 )
715         {
716                 /* Socket wurde geschlossen */
717                 Log( LOG_INFO, "%s:%d is closing the connection ...", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port));
718                 Conn_Close( Idx, NULL );
719                 return;
720         }
721
722         if( len < 0 )
723         {
724                 /* Fehler beim Lesen */
725                 Log( LOG_ALERT, "Read error on connection %d: %s!", Idx, strerror( errno ));
726                 Conn_Close( Idx, NULL );
727                 return;
728         }
729
730         /* Lesebuffer updaten */
731         My_Connections[Idx].rdatalen += len;
732         assert( My_Connections[Idx].rdatalen <= READBUFFER_LEN );
733         My_Connections[Idx].rbuf[My_Connections[Idx].rdatalen] = '\0';
734
735         if( My_Connections[Idx].rdatalen > COMMAND_LEN )
736         {
737                 /* Eine Anfrage darf(!) nicht laenger als 512 Zeichen
738                  * (incl. CR+LF!) werden; vgl. RFC 2812. Wenn soetwas
739                  * empfangen wird, wird der Client disconnectiert. */
740                 Log( LOG_ALERT, "Request too long (connection %d)!", Idx );
741                 Conn_Close( Idx, "Request too long!" );
742                 return;
743         }
744
745         /* Timestamp aktualisieren */
746         My_Connections[Idx].lastdata = time( NULL );
747
748         Handle_Buffer( Idx );
749 } /* Read_Request */
750
751
752 LOCAL VOID Handle_Buffer( CONN_ID Idx )
753 {
754         /* Daten im Lese-Puffer einer Verbindung verarbeiten. */
755
756         CHAR *ptr, *ptr1, *ptr2;
757         INT len, delta;
758         
759         /* Eine komplette Anfrage muss mit CR+LF enden, vgl.
760          * RFC 2812. Haben wir eine? */
761         ptr = strstr( My_Connections[Idx].rbuf, "\r\n" );
762
763         if( ptr ) delta = 2;
764         else
765         {
766                 /* Nicht RFC-konforme Anfrage mit nur CR oder LF? Leider
767                  * machen soetwas viele Clients, u.a. "mIRC" :-( */
768                 ptr1 = strchr( My_Connections[Idx].rbuf, '\r' );
769                 ptr2 = strchr( My_Connections[Idx].rbuf, '\n' );
770                 delta = 1;
771                 if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1;
772                 else if( ptr1 ) ptr = ptr1;
773                 else if( ptr2 ) ptr = ptr2;
774         }
775
776         if( ptr )
777         {
778                 /* Ende der Anfrage wurde gefunden */
779                 *ptr = '\0';
780                 len = ( ptr - My_Connections[Idx].rbuf ) + delta;
781                 if( len > delta )
782                 {
783                         /* Es wurde ein Request gelesen */
784                         if( ! Parse_Request( Idx, My_Connections[Idx].rbuf )) return;
785                 }
786
787                 /* Puffer anpassen */
788                 My_Connections[Idx].rdatalen -= len;
789                 memmove( My_Connections[Idx].rbuf, My_Connections[Idx].rbuf + len, My_Connections[Idx].rdatalen );
790         }
791 } /* Handle_Buffer */
792
793
794 LOCAL VOID Check_Connections( VOID )
795 {
796         /* Pruefen, ob Verbindungen noch "alive" sind. Ist dies
797          * nicht der Fall, zunaechst PING-PONG spielen und, wenn
798          * auch das nicht "hilft", Client disconnectieren. */
799
800         CLIENT *c;
801         INT i;
802
803         for( i = 0; i < MAX_CONNECTIONS; i++ )
804         {
805                 if( My_Connections[i].sock == NONE ) continue;
806
807                 c = Client_GetFromConn( i );
808                 if( c && (( c->type == CLIENT_USER ) || ( c->type == CLIENT_SERVER ) || ( c->type == CLIENT_SERVICE )))
809                 {
810                         /* verbundener User, Server oder Service */
811                         if( My_Connections[i].lastping > My_Connections[i].lastdata )
812                         {
813                                 /* es wurde bereits ein PING gesendet */
814                                 if( My_Connections[i].lastping < time( NULL ) - Conf_PongTimeout )
815                                 {
816                                         /* Timeout */
817                                         Log( LOG_INFO, "Connection %d: PING timeout.", i );
818                                         Conn_Close( i, "PING timeout" );
819                                 }
820                         }
821                         else if( My_Connections[i].lastdata < time( NULL ) - Conf_PingTimeout )
822                         {
823                                 /* es muss ein PING gesendet werden */
824                                 Log( LOG_DEBUG, "Connection %d: sending PING ...", i );
825                                 My_Connections[i].lastping = time( NULL );
826                                 Conn_WriteStr( i, "PING :%s", This_Server->nick );
827                         }
828                 }
829                 else
830                 {
831                         /* noch nicht vollstaendig aufgebaute Verbindung */
832                         if( My_Connections[i].lastdata < time( NULL ) - Conf_PingTimeout )
833                         {
834                                 /* Timeout */
835                                 Log( LOG_INFO, "Connection %d: Timeout.", i );
836                                 Conn_Close( i, "Timeout" );
837                         }
838                 }
839         }
840 } /* Check_Connections */
841
842
843 LOCAL VOID Check_Servers( VOID )
844 {
845         /* Pruefen, ob Server-Verbindungen aufgebaut werden
846          * muessen bzw. koennen */
847
848         INT idx, i, n;
849         RES_STAT *s;
850         
851         for( i = 0; i < Conf_Server_Count; i++ )
852         {
853                 /* Ist ein Hostname und Port definiert? */
854                 if(( ! Conf_Server[i].host[0] ) || ( ! Conf_Server[i].port > 0 )) continue;
855                 
856                 /* Haben wir schon eine Verbindung? */
857                 for( n = 0; n < MAX_CONNECTIONS; n++ )
858                 {
859                         if(( My_Connections[n].sock != NONE ) && ( My_Connections[n].our_server == i ))
860                         {
861                                 /* Komplett aufgebaute Verbindung? */
862                                 if( My_Connections[n].sock > NONE ) break;
863
864                                 /* IP schon aufgeloest? */
865                                 if( My_Connections[n].res_stat == NULL ) New_Server( i, n );
866                         }
867                 }
868                 if( n < MAX_CONNECTIONS ) continue;
869                 
870                 /* Wann war der letzte Connect-Versuch? */
871                 if( Conf_Server[i].lasttry > time( NULL ) - Conf_ConnectRetry ) continue;
872
873                 /* Okay, Verbindungsaufbau versuchen */
874                 Conf_Server[i].lasttry = time( NULL );
875
876                 /* Freie Connection-Struktur suschen */
877                 for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == NONE ) break;
878                 if( idx >= MAX_CONNECTIONS )
879                 {
880                         Log( LOG_ALERT, "Can't establist server connection: connection limit reached (%d)!", MAX_CONNECTIONS );
881                         return;
882                 }
883                 Log( LOG_DEBUG, "Preparing connection %d for \"%s\" ...", idx, Conf_Server[i].host );
884
885                 /* Verbindungs-Struktur initialisieren */
886                 Init_Conn_Struct( idx );
887                 My_Connections[idx].sock = SERVER_WAIT;
888                 My_Connections[idx].our_server = i;
889                 
890                 /* Hostnamen in IP aufloesen */
891                 s = ResolveName( Conf_Server[i].host );
892                 if( s )
893                 {
894                         /* Sub-Prozess wurde asyncron gestartet */
895                         My_Connections[idx].res_stat = s;
896                 }
897                 else
898                 {
899                         /* kann Namen nicht aufloesen: Connection-Struktur freigeben */
900                         Init_Conn_Struct( idx );
901                 }
902         }
903 } /* Check_Servers */
904
905
906 LOCAL VOID New_Server( INT Server, CONN_ID Idx )
907 {
908         /* Neue Server-Verbindung aufbauen */
909
910         struct sockaddr_in new_addr;
911         struct in_addr inaddr;
912         INT new_sock;
913
914         assert( Server >= 0 );
915         assert( Idx >= 0 );
916
917         /* Wurde eine gueltige IP-Adresse gefunden? */
918         if( ! Conf_Server[Server].ip[0] )
919         {
920                 /* Nein. Verbindung wieder freigeben: */
921                 Init_Conn_Struct( Idx );
922                 Log( LOG_ERR, "Can't connect to \"%s\" (connection %d): ip address unknown!", Conf_Server[Server].host, Idx );
923                 return;
924         }
925         
926         Log( LOG_INFO, "Establishing connection to \"%s\", %s (connection %d) ... ", Conf_Server[Server].host, Conf_Server[Server].ip, Idx );
927
928         if( inet_aton( Conf_Server[Server].ip, &inaddr ) == 0 )
929         {
930                 /* Konnte Adresse nicht konvertieren */
931                 Init_Conn_Struct( Idx );
932                 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 );
933                 return;
934         }
935
936         memset( &new_addr, 0, sizeof( new_addr ));
937         new_addr.sin_family = AF_INET;
938         new_addr.sin_addr = inaddr;
939         new_addr.sin_port = htons( Conf_Server[Server].port );
940
941         new_sock = socket( PF_INET, SOCK_STREAM, 0 );
942         if ( new_sock < 0 )
943         {
944                 Init_Conn_Struct( Idx );
945                 Log( LOG_ALERT, "Can't create socket: %s!", strerror( errno ));
946                 return;
947         }
948         if( connect( new_sock, (struct sockaddr *)&new_addr, sizeof( new_addr )) < 0)
949         {
950                 close( new_sock );
951                 Init_Conn_Struct( Idx );
952                 Log( LOG_ALERT, "Can't connect socket: %s!", strerror( errno ));
953                 return;
954         }
955
956         /* Client-Struktur initialisieren */
957         if( ! Client_NewLocal( Idx, inet_ntoa( new_addr.sin_addr )))
958         {
959                 close( new_sock );
960                 Init_Conn_Struct( Idx );
961                 Log( LOG_ALERT, "Can't establish connection: can't create client structure!" );
962                 return;
963         }
964         
965         /* Verbindung registrieren */
966         My_Connections[Idx].sock = new_sock;
967         My_Connections[Idx].addr = new_addr;
968         strcpy( My_Connections[Idx].host, Conf_Server[Server].host );
969
970         /* Neuen Socket registrieren */
971         FD_SET( new_sock, &My_Sockets );
972         if( new_sock > My_Max_Fd ) My_Max_Fd = new_sock;
973 } /* New_Server */
974
975
976 LOCAL VOID Init_Conn_Struct( INT Idx )
977 {
978         /* Connection-Struktur initialisieren */
979
980         My_Connections[Idx].sock = NONE;
981         My_Connections[Idx].res_stat = NULL;
982         My_Connections[Idx].host[0] = '\0';
983         My_Connections[Idx].rbuf[0] = '\0';
984         My_Connections[Idx].rdatalen = 0;
985         My_Connections[Idx].wbuf[0] = '\0';
986         My_Connections[Idx].wdatalen = 0;
987         My_Connections[Idx].our_server = -1;
988         My_Connections[Idx].lastdata = time( NULL );
989         My_Connections[Idx].lastping = 0;
990         My_Connections[Idx].lastprivmsg = time( NULL );
991 } /* Init_Conn_Struct */
992
993
994 LOCAL RES_STAT *ResolveAddr( struct sockaddr_in *Addr )
995 {
996         /* IP (asyncron!) aufloesen. Bei Fehler, z.B. wenn der
997          * Child-Prozess nicht erzeugt werden kann, wird NULL geliefert.
998          * Der Host kann dann nicht aufgeloest werden. */
999
1000         RES_STAT *s;
1001         INT pid;
1002
1003         /* Speicher anfordern */
1004         s = malloc( sizeof( RES_STAT ));
1005         if( ! s )
1006         {
1007                 Log( LOG_ALERT, "Resolver: Can't alloc memory!" );
1008                 return NULL;
1009         }
1010
1011         /* Pipe fuer Antwort initialisieren */
1012         if( pipe( s->pipe ) != 0 )
1013         {
1014                 free( s );
1015                 Log( LOG_ALERT, "Resolver: Can't create output pipe: %s!", strerror( errno ));
1016                 return NULL;
1017         }
1018
1019         /* Sub-Prozess erzeugen */
1020         pid = fork( );
1021         if( pid > 0 )
1022         {
1023                 /* Haupt-Prozess */
1024                 Log( LOG_DEBUG, "Resolver for %s created (PID %d).", inet_ntoa( Addr->sin_addr ), pid );
1025                 FD_SET( s->pipe[0], &My_Resolvers );
1026                 if( s->pipe[0] > My_Max_Fd ) My_Max_Fd = s->pipe[0];
1027                 s->pid = pid;
1028                 return s;
1029         }
1030         else if( pid == 0 )
1031         {
1032                 /* Sub-Prozess */
1033                 Log_Init_Resolver( );
1034                 Do_ResolveAddr( Addr, s->pipe[1] );
1035                 Log_Exit_Resolver( );
1036                 exit( 0 );
1037         }
1038         else
1039         {
1040                 /* Fehler */
1041                 free( s );
1042                 Log( LOG_ALERT, "Resolver: Can't fork: %s!", strerror( errno ));
1043                 return NULL;
1044         }
1045 } /* ResolveAddr */
1046
1047
1048 LOCAL RES_STAT *ResolveName( CHAR *Host )
1049 {
1050         /* Hostnamen (asyncron!) aufloesen. Bei Fehler, z.B. wenn der
1051         * Child-Prozess nicht erzeugt werden kann, wird NULL geliefert.
1052         * Der Host kann dann nicht aufgeloest werden. */
1053
1054         RES_STAT *s;
1055         INT pid;
1056
1057         /* Speicher anfordern */
1058         s = malloc( sizeof( RES_STAT ));
1059         if( ! s )
1060         {
1061                 Log( LOG_ALERT, "Resolver: Can't alloc memory!" );
1062                 return NULL;
1063         }
1064
1065         /* Pipe fuer Antwort initialisieren */
1066         if( pipe( s->pipe ) != 0 )
1067         {
1068                 free( s );
1069                 Log( LOG_ALERT, "Resolver: Can't create output pipe: %s!", strerror( errno ));
1070                 return NULL;
1071         }
1072
1073         /* Sub-Prozess erzeugen */
1074         pid = fork( );
1075         if( pid > 0 )
1076         {
1077                 /* Haupt-Prozess */
1078                 Log( LOG_DEBUG, "Resolver for \"%s\" created (PID %d).", Host, pid );
1079                 FD_SET( s->pipe[0], &My_Resolvers );
1080                 if( s->pipe[0] > My_Max_Fd ) My_Max_Fd = s->pipe[0];
1081                 s->pid = pid;
1082                 return s;
1083         }
1084         else if( pid == 0 )
1085         {
1086                 /* Sub-Prozess */
1087                 Log_Init_Resolver( );
1088                 Do_ResolveName( Host, s->pipe[1] );
1089                 Log_Exit_Resolver( );
1090                 exit( 0 );
1091         }
1092         else
1093         {
1094                 /* Fehler */
1095                 free( s );
1096                 Log( LOG_ALERT, "Resolver: Can't fork: %s!", strerror( errno ));
1097                 return NULL;
1098         }
1099 } /* ResolveName */
1100
1101
1102 LOCAL VOID Do_ResolveAddr( struct sockaddr_in *Addr, INT w_fd )
1103 {
1104         /* Resolver Sub-Prozess: IP aufloesen und Ergebnis in Pipe schreiben. */
1105
1106         CHAR hostname[HOST_LEN];
1107         struct hostent *h;
1108
1109         Log_Resolver( LOG_DEBUG, "Now resolving %s ...", inet_ntoa( Addr->sin_addr ));
1110
1111         /* Namen aufloesen */
1112         h = gethostbyaddr( (CHAR *)&Addr->sin_addr, sizeof( Addr->sin_addr ), AF_INET );
1113         if( h ) strcpy( hostname, h->h_name );
1114         else
1115         {
1116                 Log_Resolver( LOG_WARNING, "Can't resolve address %s: code %s!", inet_ntoa( Addr->sin_addr ), Resolv_Error( h_errno ));
1117                 strcpy( hostname, inet_ntoa( Addr->sin_addr ));
1118         }
1119
1120         /* Antwort an Parent schreiben */
1121         if( write( w_fd, hostname, strlen( hostname ) + 1 ) != ( strlen( hostname ) + 1 ))
1122         {
1123                 Log_Resolver( LOG_ALERT, "Resolver: Can't write to parent: %s!", strerror( errno ));
1124                 close( w_fd );
1125                 return;
1126         }
1127
1128         Log_Resolver( LOG_DEBUG, "Ok, translated %s to \"%s\".", inet_ntoa( Addr->sin_addr ), hostname );
1129 } /* Do_ResolveAddr */
1130
1131
1132 LOCAL VOID Do_ResolveName( CHAR *Host, INT w_fd )
1133 {
1134         /* Resolver Sub-Prozess: Name aufloesen und Ergebnis in Pipe schreiben. */
1135
1136         CHAR ip[16];
1137         struct hostent *h;
1138         struct in_addr *addr;
1139
1140         Log_Resolver( LOG_DEBUG, "Now resolving \"%s\" ...", Host );
1141
1142         /* Namen aufloesen */
1143         h = gethostbyname( Host );
1144         if( h )
1145         {
1146                 addr = (struct in_addr *)h->h_addr;
1147                 strcpy( ip, inet_ntoa( *addr ));
1148         }
1149         else
1150         {
1151                 Log_Resolver( LOG_WARNING, "Can't resolve \"%s\": %s!", Host, Resolv_Error( h_errno ));
1152                 strcpy( ip, "" );
1153         }
1154
1155         /* Antwort an Parent schreiben */
1156         if( write( w_fd, ip, strlen( ip ) + 1 ) != ( strlen( ip ) + 1 ))
1157         {
1158                 Log_Resolver( LOG_ALERT, "Resolver: Can't write to parent: %s!", strerror( errno ));
1159                 close( w_fd );
1160                 return;
1161         }
1162
1163         if( ip[0] ) Log_Resolver( LOG_DEBUG, "Ok, translated \"%s\" to %s.", Host, ip );
1164 } /* Do_ResolveName */
1165
1166
1167 LOCAL VOID Read_Resolver_Result( INT r_fd )
1168 {
1169         /* Ergebnis von Resolver Sub-Prozess aus Pipe lesen
1170         * und entsprechende Connection aktualisieren */
1171
1172         CHAR result[HOST_LEN];
1173         CLIENT *c;
1174         INT len, i;
1175
1176         FD_CLR( r_fd, &My_Resolvers );
1177
1178         /* Anfrage vom Parent lesen */
1179         len = read( r_fd, result, HOST_LEN);
1180         if( len < 0 )
1181         {
1182                 /* Fehler beim Lesen aus der Pipe */
1183                 close( r_fd );
1184                 Log( LOG_ALERT, "Resolver: Can't read result: %s!", strerror( errno ));
1185                 return;
1186         }
1187         result[len] = '\0';
1188
1189         /* zugehoerige Connection suchen */
1190         for( i = 0; i < MAX_CONNECTIONS; i++ )
1191         {
1192                 if(( My_Connections[i].sock != NONE ) && ( My_Connections[i].res_stat ) && ( My_Connections[i].res_stat->pipe[0] == r_fd )) break;
1193         }
1194         if( i >= MAX_CONNECTIONS )
1195         {
1196                 /* Opsa! Keine passende Connection gefunden!? Vermutlich
1197                  * wurde sie schon wieder geschlossen. */
1198                 close( r_fd );
1199                 Log( LOG_DEBUG, "Resolver: Got result for unknown connection!?" );
1200                 return;
1201         }
1202
1203         /* Aufraeumen */
1204         close( My_Connections[i].res_stat->pipe[0] );
1205         close( My_Connections[i].res_stat->pipe[1] );
1206         free( My_Connections[i].res_stat );
1207         My_Connections[i].res_stat = NULL;
1208
1209         if( My_Connections[i].sock > NONE )
1210         {
1211                 /* Eingehende Verbindung: Hostnamen setzen */
1212                 strcpy( My_Connections[i].host, result );
1213                 c = Client_GetFromConn( i );
1214                 if( c ) Client_SetHostname( c, result );
1215         }
1216         else
1217         {
1218                 /* Ausgehende Verbindung (=Server): IP setzen */
1219                 assert( My_Connections[i].our_server >= 0 );
1220                 strcpy( Conf_Server[My_Connections[i].our_server].ip, result );
1221         }
1222 } /* Read_Resolver_Result */
1223
1224
1225 LOCAL CHAR *Resolv_Error( INT H_Error )
1226 {
1227         /* Fehlerbeschreibung fuer H_Error liefern */
1228
1229         switch( H_Error )
1230         {
1231                 case HOST_NOT_FOUND:
1232                         return "host not found";
1233                 case NO_ADDRESS:
1234                         return "name valid but no IP address defined";
1235                 case NO_RECOVERY:
1236                         return "name server error";
1237                 case TRY_AGAIN:
1238                         return "name server temporary not available";
1239                 default:
1240                         return "unknown error";
1241         }
1242 } /* Resolv_Error */
1243
1244
1245 /* -eof- */