2 * ngIRCd -- The Next Generation IRC Daemon
3 * Copyright (c)2001 by Alexander Barton (alex@barton.de)
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 comBase beteiligten Autoren finden Sie in der Datei AUTHORS.
12 * $Id: conn.c,v 1.4 2001/12/13 02:04:16 alex Exp $
14 * connect.h: Verwaltung aller Netz-Verbindungen ("connections")
17 * Revision 1.4 2001/12/13 02:04:16 alex
18 * - boesen "Speicherschiesser" in Log() gefixt.
20 * Revision 1.3 2001/12/13 01:33:09 alex
21 * - Conn_Handler() unterstuetzt nun einen Timeout.
22 * - fuer Verbindungen werden keine FILE-Handles mehr benutzt.
23 * - kleinere "Code Cleanups" ;-)
25 * Revision 1.2 2001/12/12 23:32:02 alex
26 * - diverse Erweiterungen und Verbesserungen (u.a. sind nun mehrere
27 * Verbindungen und Listen-Sockets moeglich).
29 * Revision 1.1 2001/12/12 17:18:38 alex
30 * - Modul zur Verwaltung aller Netzwerk-Verbindungen begonnen.
44 #include <sys/socket.h>
46 #include <sys/types.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
51 #include <stdint.h> /* u.a. fuer Mac OS X */
62 #define MAX_CONNECTIONS 100
65 typedef struct _Connection
67 INT sock; /* Socket Handle */
68 struct sockaddr_in addr; /* Adresse des Client */
72 LOCAL VOID Handle_Socket( INT sock );
74 LOCAL VOID New_Connection( INT Sock );
76 LOCAL INT Socket2Index( INT Sock );
78 LOCAL VOID Close_Connection( INT Idx );
79 LOCAL VOID Read_Request( INT Idx );
81 LOCAL BOOLEAN Send( INT Idx, CHAR *Data );
84 LOCAL fd_set My_Listener;
85 LOCAL fd_set My_Sockets;
89 LOCAL CONNECTION My_Connections[MAX_CONNECTIONS];
92 GLOBAL VOID Conn_Init( VOID )
96 /* zu Beginn haben wir keine Verbindungen */
97 FD_ZERO( &My_Listener );
98 FD_ZERO( &My_Sockets );
102 /* Connection-Struktur initialisieren */
103 for( i = 0; i < MAX_CONNECTIONS; i++ )
105 My_Connections[i].sock = -1;
110 GLOBAL VOID Conn_Exit( VOID )
114 /* Sockets schliessen */
115 for( i = 0; i < My_Max_Fd + 1; i++ )
117 if( FD_ISSET( i, &My_Sockets ))
119 for( idx = 0; idx < MAX_CONNECTIONS; idx++ )
121 if( My_Connections[idx].sock == i ) break;
123 if( idx < MAX_CONNECTIONS ) Close_Connection( idx );
124 else if( FD_ISSET( i, &My_Listener ))
127 Log( LOG_INFO, "Closed listening socket %d.", i );
132 Log( LOG_WARNING, "Unknown connection %d closed.", i );
139 GLOBAL BOOLEAN Conn_New_Listener( CONST INT Port )
141 /* Neuen Listen-Socket erzeugen: der Server wartet dann
142 * auf dem angegebenen Port auf Verbindungen. */
144 struct sockaddr_in addr;
147 /* Server-"Listen"-Socket initialisieren */
148 memset( &addr, 0, sizeof( addr ));
149 addr.sin_family = AF_INET;
150 addr.sin_port = htons( Port );
151 addr.sin_addr.s_addr = htonl( INADDR_ANY );
153 /* Socket erzeugen */
154 sock = socket( PF_INET, SOCK_STREAM, 0);
157 Log( LOG_ALERT, "Can't create socket: %s", strerror( errno ));
161 /* Socket-Optionen setzen */
162 if( fcntl( sock, F_SETFL, O_NONBLOCK ) != 0 )
164 Log( LOG_ALERT, "Can't enable non-blocking mode: %s", strerror( errno ));
168 if( setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof( on )) != 0)
170 Log( LOG_CRIT, "Can't set socket options: %s", strerror( errno ));
171 /* dieser Fehler kann ignoriert werden. */
175 if( bind( sock, (struct sockaddr *)&addr, (socklen_t)sizeof( addr )) != 0 )
177 Log( LOG_ALERT, "Can't bind socket: %s", strerror( errno ));
182 /* in "listen mode" gehen :-) */
183 if( listen( sock, 10 ) != 0 )
185 Log( LOG_ALERT, "Can't listen on soecket: %s", strerror( errno ));
190 /* Neuen Listener in Strukturen einfuegen */
191 FD_SET( sock, &My_Listener );
192 FD_SET( sock, &My_Sockets );
194 if( sock > My_Max_Fd ) My_Max_Fd = sock;
196 Log( LOG_INFO, "Now listening on port %d, socked %d.", Port, sock );
199 } /* Conn_New_Listener */
202 GLOBAL VOID Conn_Handler( INT Timeout )
208 /* Timeout initialisieren */
212 read_sockets = My_Sockets;
213 if( select( My_Max_Fd + 1, &read_sockets, NULL, NULL, &tv ) == -1 )
215 if( errno != EINTR ) Log( LOG_ALERT, "select(): %s", strerror( errno ));
219 for( i = 0; i < My_Max_Fd + 1; i++ )
221 if( FD_ISSET( i, &read_sockets )) Handle_Socket( i );
226 LOCAL VOID Handle_Socket( INT Sock )
228 /* Aktivitaet auf einem Socket verarbeiten */
232 if( FD_ISSET( Sock, &My_Listener ))
234 /* es ist einer unserer Listener-Sockets: es soll
235 * also eine neue Verbindung aufgebaut werden. */
237 New_Connection( Sock );
241 /* Ein Client Socket: entweder ein User oder Server */
243 idx = Socket2Index( Sock );
246 } /* Handle_Socket */
249 LOCAL VOID New_Connection( INT Sock )
251 struct sockaddr_in new_addr;
252 INT new_sock, new_sock_len, idx;
254 new_sock_len = sizeof( new_addr );
255 new_sock = accept( Sock, (struct sockaddr *)&new_addr, (socklen_t *)&new_sock_len );
258 Log( LOG_CRIT, "Can't accept connection: %s", strerror( errno ));
262 /* Freie Connection-Struktur suschen */
263 for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock < 0 ) break;
264 if( idx >= MAX_CONNECTIONS )
266 Log( LOG_ALERT, "Can't accept connection: limit (%d) reached!", MAX_CONNECTIONS );
271 /* Verbindung registrieren */
272 My_Connections[idx].sock = new_sock;
273 My_Connections[idx].addr = new_addr;
275 /* Neuen Socket registrieren */
276 FD_SET( new_sock, &My_Sockets );
278 if( new_sock > My_Max_Fd ) My_Max_Fd = new_sock;
280 Send( idx, "hello world!\n" );
282 Log( LOG_INFO, "Accepted connection from %s:%d.", inet_ntoa( new_addr.sin_addr ), ntohs( new_addr.sin_port));
283 } /* New_Connection */
286 LOCAL INT Socket2Index( INT Sock )
290 for( idx = 0; idx < MAX_CONNECTIONS; idx++ ) if( My_Connections[idx].sock == Sock ) break;
291 assert( idx < MAX_CONNECTIONS );
297 LOCAL VOID Close_Connection( INT Idx )
299 /* Verbindung schlie§en */
301 assert( My_Connections[Idx].sock >= 0 );
303 if( close( My_Connections[Idx].sock ) != 0 )
305 Log( LOG_ERR, "Error closing connection with %s:%d - %s", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port), strerror( errno ));
309 Log( LOG_INFO, "Closed connection with %s:%d.", inet_ntoa( My_Connections[Idx].addr.sin_addr ), ntohs( My_Connections[Idx].addr.sin_port ));
312 FD_CLR( My_Connections[Idx].sock, &My_Sockets );
313 My_Connections[Idx].sock = -1;
314 } /* Close_Connection */
317 LOCAL VOID Read_Request( INT Idx )
319 /* Daten von Socket einlesen und entsprechend behandeln.
320 * Tritt ein Fehler auf, so wird der Socket geschlossen. */
327 len = recv( My_Connections[Idx].sock, buffer, SIZE, 0 );
331 /* Socket wurde geschlossen */
332 Close_Connection( Idx );
338 /* Fehler beim Lesen */
339 Log( LOG_ALERT, "Read error on socket %d!", My_Connections[Idx].sock );
340 Close_Connection( Idx );
344 ngt_Trim_Str( buffer );
345 printf( " in: '%s'\n", buffer );
349 LOCAL BOOLEAN Send( INT Idx, CHAR *Data )
351 /* Daten in Socket schreiben, ggf. in mehreren Stuecken. Tritt
352 * ein Fehler auf, so wird die Verbindung beendet und FALSE
353 * als Rueckgabewert geliefert. */
358 len = strlen( Data );
362 n = send( My_Connections[Idx].sock, Data + sent, len - sent, 0 );
365 /* Oops, ein Fehler! */
366 Log( LOG_ALERT, "Write error on socket %d!", My_Connections[Idx].sock );
367 Close_Connection( Idx );