#include "portab.h"
#include "io.h"
-static char UNUSED id[] = "$Id: conn.c,v 1.170 2005/08/15 23:02:40 alex Exp $";
+static char UNUSED id[] = "$Id: conn.c,v 1.182 2005/09/24 02:20:00 fw Exp $";
#include "imp.h"
#include <assert.h>
static void server_login PARAMS((CONN_ID idx));
+static void cb_Read_Resolver_Result PARAMS(( int sock, UNUSED short what));
static void cb_clientserver PARAMS((int sock, short what));
static void
}
-static void
-FreeRes_stat( CONNECTION *c )
-{
- assert( c != NULL );
- assert( c->res_stat != NULL );
-
- if (!c->res_stat) return;
-
- io_close( c->res_stat->pipe[0] );
-
- array_free(&c->res_stat->buffer);
- free( c->res_stat );
- c->res_stat = NULL;
-}
-
-
GLOBAL void
Conn_Init( void )
{
{
/* Close down all listening sockets */
int *fd;
- unsigned int arraylen;
+ size_t arraylen;
#ifdef ZEROCONF
Rendezvous_UnregisterListeners( );
#endif
arraylen = array_length(&My_Listeners, sizeof (int));
- Log( LOG_INFO, "Shutting down all listening sockets (%d)...", arraylen );
+ Log( LOG_INFO, "Shutting down all listening sockets (%d total)...", arraylen );
+ fd = array_start(&My_Listeners);
while(arraylen--) {
- fd = (int*) array_get(&My_Listeners, sizeof (int), arraylen);
- if (fd) {
- close(*fd);
+ assert(fd);
+ assert(*fd >= 0);
+ io_close(*fd);
#ifdef DEBUG
- Log( LOG_DEBUG, "Listening socket %d closed.", *fd );
- } else {
- Log( LOG_DEBUG, "array_get pos %d returned NULL", arraylen );
+ Log( LOG_DEBUG, "Listening socket %d closed.", *fd );
#endif
- }
+ fd++;
}
array_free(&My_Listeners);
} /* Conn_ExitListeners */
if ( My_Connections[i].sock <= NONE )
continue;
- if ( My_Connections[i].res_stat ) {
+ if (Resolve_INPROGRESS(&My_Connections[i].res_stat)) {
/* wait for completion of Resolver Sub-Process */
io_event_del( My_Connections[i].sock, IO_WANTREAD );
continue;
/* Send statistics to client if registered as user: */
if ((c != NULL) && (Client_Type(c) == CLIENT_USER)) {
Conn_WriteStr( Idx,
- "NOTICE %s :%sConnection statistics: client %.1f kb, server %.1f kb.",
- Client_ID(Client_ThisServer()), NOTICE_TXTPREFIX,
+ ":%s NOTICE %s :%sConnection statistics: client %.1f kb, server %.1f kb.",
+ Client_ID(Client_ThisServer()), Client_ID(c),
+ NOTICE_TXTPREFIX,
(double)My_Connections[Idx].bytes_in / 1024,
(double)My_Connections[Idx].bytes_out / 1024);
}
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 );
}
- /* Is there a resolver sub-process running? */
- if( My_Connections[Idx].res_stat )
- FreeRes_stat( &My_Connections[Idx] );
+ /* cancel running resolver */
+ if (Resolve_INPROGRESS(&My_Connections[Idx].res_stat)) {
+ Resolve_Shutdown(&My_Connections[Idx].res_stat);
+ }
/* Servers: Modify time of next connect attempt? */
Conf_UnsetServer( Idx );
int len;
unsigned int wdatalen;
- Log(LOG_DEBUG, "Handle_Write");
assert( Idx > NONE );
if ( My_Connections[Idx].sock < 0 ) {
- Log(LOG_WARNING, "Handle_Write() on closed socket, idx %d", Idx);
+#ifdef DEBUG
+ Log(LOG_DEBUG,
+ "Handle_Write() on closed socket, connection %d", Idx);
+#endif
return false;
}
assert( My_Connections[Idx].sock > NONE );
+#ifdef DEBUG
+ Log(LOG_DEBUG, "Handle_Write() called for connection %d ...", Idx);
+#endif
+
wdatalen = array_bytes(&My_Connections[Idx].wbuf );
#ifdef ZLIB
- if(( wdatalen == 0 ) && ( ! array_bytes(&My_Connections[Idx].zip.wbuf))) {
+ if (wdatalen == 0 && !array_bytes(&My_Connections[Idx].zip.wbuf)) {
io_event_del(My_Connections[Idx].sock, IO_WANTWRITE );
return true;
}
- /* write buffer empty, but not compression buf? -> flush compression buf. */
- if( wdatalen == 0 ) Zip_Flush( Idx );
+ /* write buffer empty, but not compression buffer?
+ * -> flush compression buffer! */
+ if (wdatalen == 0)
+ Zip_Flush(Idx);
#else
- if( wdatalen == 0 ) {
+ if (wdatalen == 0) {
io_event_del(My_Connections[Idx].sock, IO_WANTWRITE );
return true;
}
#endif
- wdatalen = array_bytes(&My_Connections[Idx].wbuf ); /* Zip_Flush may change wbuf */
- len = write( My_Connections[Idx].sock, array_start(&My_Connections[Idx].wbuf), wdatalen );
+ /* Zip_Flush() may have changed the write buffer ... */
+ wdatalen = array_bytes(&My_Connections[Idx].wbuf);
+
+ len = write(My_Connections[Idx].sock,
+ array_start(&My_Connections[Idx].wbuf), wdatalen );
+
if( len < 0 ) {
- if( errno == EAGAIN || errno == EINTR)
+ if (errno == EAGAIN || errno == EINTR)
return true;
- Log( LOG_ERR, "Write error on connection %d (socket %d): %s!", Idx,
- My_Connections[Idx].sock, strerror( errno ));
- Conn_Close( Idx, "Write error!", NULL, false );
+ Log(LOG_ERR, "Write error on connection %d (socket %d): %s!",
+ Idx, My_Connections[Idx].sock, strerror(errno));
+ Conn_Close(Idx, "Write error!", NULL, false);
return false;
}
#endif
struct sockaddr_in new_addr;
int new_sock, new_sock_len;
- RES_STAT *s;
CONN_ID idx;
CLIENT *c;
POINTER *ptr;
/* Hostnamen ermitteln */
strlcpy( My_Connections[idx].host, inet_ntoa( new_addr.sin_addr ), sizeof( My_Connections[idx].host ));
Client_SetHostname( c, My_Connections[idx].host );
-#ifdef IDENTAUTH
- s = Resolve_Addr( &new_addr, My_Connections[idx].sock );
-#else
- s = Resolve_Addr( &new_addr );
-#endif
- /* resolver process has been started */
- if( s ) My_Connections[idx].res_stat = s;
+
+ Resolve_Addr(&My_Connections[idx].res_stat, &new_addr,
+ My_Connections[idx].sock, cb_Read_Resolver_Result);
/* Penalty-Zeit setzen */
Conn_SetPenalty( idx, 4 );
int len;
char readbuf[1024];
+ CLIENT *c;
assert( Idx > NONE );
assert( My_Connections[Idx].sock > NONE );
}
}
- /* Connection-Statistik aktualisieren */
+ /* Update connection statistics */
My_Connections[Idx].bytes_in += len;
- /* Timestamp aktualisieren */
- My_Connections[Idx].lastdata = time( NULL );
-
- Handle_Buffer( Idx );
+ /* Update timestamp of last data received if this connection is
+ * registered as a user, server or service connection. Don't update
+ * otherwise, so users have at least Conf_PongTimeout seconds time to
+ * register with the IRC server -- see Check_Connections(). */
+ c = Client_GetFromConn(Idx);
+ if (c && (Client_Type(c) == CLIENT_USER
+ || Client_Type(c) == CLIENT_SERVER
+ || Client_Type(c) == CLIENT_SERVICE))
+ My_Connections[Idx].lastdata = time(NULL);
+
+ /* Look at the data in the (read-) buffer of this connection */
+ Handle_Buffer(Idx);
} /* Read_Request */
#endif
char *ptr;
int len, delta;
- bool action, result;
+ bool result;
#ifdef ZLIB
bool old_z;
#endif
result = false;
- do {
+ for (;;) {
/* Check penalty */
if( My_Connections[Idx].delaytime > time( NULL )) return result;
#ifdef ZLIB
}
#endif
- action = false;
if( ! ptr )
break;
return false;
}
+ if (len <= 2) { /* request was empty (only '\r\n') */
+ array_moveleft(&My_Connections[Idx].rbuf, 1, delta); /* delta is either 1 or 2 */
+ break;
+ }
#ifdef ZLIB
/* remember if stream is already compressed */
old_z = My_Connections[Idx].options & CONN_ZIP;
#endif
- if( len > delta )
- {
- /* A Request was read */
- My_Connections[Idx].msg_in++;
- if( ! Parse_Request( Idx, (char*)array_start(&My_Connections[Idx].rbuf) )) return false;
- else action = true;
+ My_Connections[Idx].msg_in++;
+ if (!Parse_Request(Idx, (char*)array_start(&My_Connections[Idx].rbuf) ))
+ return false;
+
+ result = true;
- array_moveleft(&My_Connections[Idx].rbuf, 1, len);
+ array_moveleft(&My_Connections[Idx].rbuf, 1, len);
#ifdef DEBUG
- Log(LOG_DEBUG, "%d byte left in rbuf", array_bytes(&My_Connections[Idx].rbuf));
+ Log(LOG_DEBUG,
+ "Connection %d: %d bytes left in read buffer.",
+ Idx, array_bytes(&My_Connections[Idx].rbuf));
#endif
- }
-
#ifdef ZLIB
- if(( ! old_z ) && ( My_Connections[Idx].options & CONN_ZIP ) && ( array_bytes(&My_Connections[Idx].rbuf) > 0 ))
+ if(( ! old_z ) && ( My_Connections[Idx].options & CONN_ZIP ) &&
+ ( array_bytes(&My_Connections[Idx].rbuf) > 0 ))
{
/* The last Command activated Socket-Compression.
* Data that was read after that needs to be copied to Unzip-buf
* for decompression */
if( array_bytes(&My_Connections[Idx].rbuf)> ZREADBUFFER_LEN ) {
- /* No space left */
- Log( LOG_ALERT, "Can't move receive buffer: No space left in unzip buffer (need %d bytes)!", array_bytes(&My_Connections[Idx].rbuf ));
+ Log( LOG_ALERT, "Connection %d: No space left in unzip buf (need %u bytes)!",
+ Idx, array_bytes(&My_Connections[Idx].rbuf ));
return false;
}
if (!array_copy( &My_Connections[Idx].zip.rbuf, &My_Connections[Idx].rbuf ))
array_trunc(&My_Connections[Idx].rbuf);
#ifdef DEBUG
- Log( LOG_DEBUG, "Moved already received data (%d bytes) to uncompression buffer.", array_bytes(&My_Connections[Idx].zip.rbuf));
+ Log( LOG_DEBUG, "Moved already received data (%u bytes) to uncompression buffer.",
+ array_bytes(&My_Connections[Idx].zip.rbuf));
#endif /* DEBUG */
}
#endif /* ZLIB */
- if( action ) result = true;
- } while( action );
+ }
return result;
} /* Handle_Buffer */
CONN_ID i;
for( i = 0; i < Pool_Size; i++ ) {
- if( My_Connections[i].sock == NONE ) continue;
+ if (My_Connections[i].sock < 0)
+ continue;
c = Client_GetFromConn( i );
if( c && (( Client_Type( c ) == CLIENT_USER ) || ( Client_Type( c ) == CLIENT_SERVER ) || ( Client_Type( c ) == CLIENT_SERVICE )))
}
else
{
- /* connection is not fully established yet */
- if( My_Connections[i].lastdata < time( NULL ) - Conf_PingTimeout )
- {
- /* Timeout */
+ /* The connection is not fully established yet, so
+ * we don't do the PING-PONG game here but instead
+ * disconnect the client after "a short time" if it's
+ * still not registered. */
+
+ if (My_Connections[i].lastdata <
+ time(NULL) - Conf_PongTimeout) {
#ifdef DEBUG
- Log( LOG_DEBUG, "Connection %d timed out ...", i );
+ Log(LOG_DEBUG,
+ "Unregistered connection %d timed out ...",
+ i);
#endif
- Conn_Close( i, NULL, "Timeout", false );
+ Conn_Close(i, NULL, "Timeout", false);
}
}
}
{
/* Check if we can establish further server links */
- RES_STAT *s;
CONN_ID idx;
int i, n;
- /* Serach all connections, are there results from the resolver? */
- for( idx = 0; idx < Pool_Size; idx++ )
- {
+ /* Search all connections, are there results from the resolver? */
+ for( idx = 0; idx < Pool_Size; idx++ ) {
if( My_Connections[idx].sock != SERVER_WAIT ) continue;
/* IP resolved? */
- if( My_Connections[idx].res_stat == NULL ) New_Server( Conf_GetServer( idx ), idx );
+ if (Resolve_SUCCESS(&My_Connections[idx].res_stat))
+ New_Server(Conf_GetServer( idx ), idx);
}
/* Check all configured servers */
- for( i = 0; i < MAX_SERVERS; i++ )
- {
+ for( i = 0; i < MAX_SERVERS; i++ ) {
/* Valid outgoing server which isn't already connected or disabled? */
- if(( ! Conf_Server[i].host[0] ) || ( ! Conf_Server[i].port > 0 ) || ( Conf_Server[i].conn_id > NONE ) || ( Conf_Server[i].flags & CONF_SFLAG_DISABLED )) continue;
+ if(( ! Conf_Server[i].host[0] ) || ( ! Conf_Server[i].port > 0 ) ||
+ ( Conf_Server[i].conn_id > NONE ) || ( Conf_Server[i].flags & CONF_SFLAG_DISABLED ))
+ continue;
/* Is there already a connection in this group? */
- if( Conf_Server[i].group > NONE )
- {
- for( n = 0; n < MAX_SERVERS; n++ )
- {
- if( n == i ) continue;
- if(( Conf_Server[n].conn_id > NONE ) && ( Conf_Server[n].group == Conf_Server[i].group )) break;
+ if( Conf_Server[i].group > NONE ) {
+ for (n = 0; n < MAX_SERVERS; n++) {
+ if (n == i) continue;
+ if ((Conf_Server[n].conn_id > NONE) &&
+ (Conf_Server[n].group == Conf_Server[i].group))
+ break;
}
- if( n < MAX_SERVERS ) continue;
+ if (n < MAX_SERVERS) continue;
}
/* Check last connect attempt? */
- if( Conf_Server[i].lasttry > time( NULL ) - Conf_ConnectRetry ) continue;
+ if( Conf_Server[i].lasttry > time( NULL ) - Conf_ConnectRetry )
+ continue;
/* Okay, try to connect now */
Conf_Server[i].lasttry = time( NULL );
/* Search free connection structure */
for( idx = 0; idx < Pool_Size; idx++ ) if( My_Connections[idx].sock == NONE ) break;
- if( idx >= Pool_Size )
- {
- Log( LOG_ALERT, "Can't establist server connection: connection limit reached (%d)!", Pool_Size );
+ if (idx >= Pool_Size) {
+ Log( LOG_ALERT, "Can't establist server connection: connection limit reached (%d)!",
+ Pool_Size );
return;
}
#ifdef DEBUG
Log( LOG_DEBUG, "Preparing connection %d for \"%s\" ...", idx, Conf_Server[i].host );
#endif
- /* Verbindungs-Struktur initialisieren */
Init_Conn_Struct( idx );
My_Connections[idx].sock = SERVER_WAIT;
Conf_Server[i].conn_id = idx;
/* Resolve Hostname. If this fails, try to use it as an IP address */
strlcpy( Conf_Server[i].ip, Conf_Server[i].host, sizeof( Conf_Server[i].ip ));
strlcpy( My_Connections[idx].host, Conf_Server[i].host, sizeof( My_Connections[idx].host ));
- s = Resolve_Name( Conf_Server[i].host );
- /* resolver process running? */
- if( s ) My_Connections[idx].res_stat = s;
+ assert(Resolve_Getfd(&My_Connections[idx].res_stat) < 0);
+
+ Resolve_Name(&My_Connections[idx].res_stat, Conf_Server[i].host, cb_Read_Resolver_Result);
}
} /* Check_Servers */
assert( Server > NONE );
assert( Idx > NONE );
- /* Did we get a valid IP address? */
- if( ! Conf_Server[Server].ip[0] ) {
- /* No. Free connection structure and abort: */
- Log( LOG_ERR, "Can't connect to \"%s\" (connection %d): ip address unknown!", Conf_Server[Server].host, Idx );
- goto out;
- }
-
- 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 );
+ Log( LOG_INFO, "Establishing connection to \"%s\", %s, port %d ... ", Conf_Server[Server].host,
+ Conf_Server[Server].ip, Conf_Server[Server].port );
#ifdef HAVE_INET_ATON
if( inet_aton( Conf_Server[Server].ip, &inaddr ) == 0 )
My_Connections[Idx].sock = NONE;
My_Connections[Idx].lastdata = now;
My_Connections[Idx].lastprivmsg = now;
+ Resolve_Init(&My_Connections[Idx].res_stat);
} /* Init_Conn_Struct */
} /* Init_Socket */
-GLOBAL
-void Read_Resolver_Result( int r_fd )
+static void
+cb_Read_Resolver_Result( int r_fd, UNUSED short events )
{
/* Read result of resolver sub-process from pipe and update the
* apropriate connection/client structure(s): hostname and/or
* IDENT user name.*/
CLIENT *c;
- int bytes_read, i, n;
- unsigned int len;
- RES_STAT *s;
- char *ptr;
- char *bufptr;
- char readbuf[HOST_LEN];
+ int i, n;
+ size_t len;
+ char *identptr;
+#ifdef IDENTAUTH
+ char readbuf[HOST_LEN + 2 + CLIENT_USER_LEN];
+#else
+ char readbuf[HOST_LEN + 1];
+#endif
+
+#ifdef DEBUG
+ Log( LOG_DEBUG, "Resolver: Got callback on fd %d, events %d", r_fd, events );
+#endif
- Log( LOG_DEBUG, "Resolver: started, fd %d", r_fd );
/* Search associated connection ... */
- for( i = 0; i < Pool_Size; i++ )
- {
+ for( i = 0; i < Pool_Size; i++ ) {
if(( My_Connections[i].sock != NONE )
- && ( My_Connections[i].res_stat != NULL )
- && ( My_Connections[i].res_stat->pipe[0] == r_fd ))
+ && ( Resolve_Getfd(&My_Connections[i].res_stat) == r_fd ))
break;
}
- if( i >= Pool_Size )
- {
+ if( i >= Pool_Size ) {
/* Ops, none found? Probably the connection has already
* been closed!? We'll ignore that ... */
io_close( r_fd );
#ifdef DEBUG
- Log( LOG_DEBUG, "Resolver: Got result for unknown connection!?" );
+ Log( LOG_DEBUG, "Resolver: Got callback for unknown connection!?" );
#endif
return;
}
- /* Get resolver structure */
- s = My_Connections[i].res_stat;
- assert( s != NULL );
-
/* Read result from pipe */
- bytes_read = read( r_fd, readbuf, sizeof readbuf -1 );
- if( bytes_read < 0 ) {
- /* Error! */
- Log( LOG_CRIT, "Resolver: Can't read result: %s!", strerror( errno ));
- FreeRes_stat( &My_Connections[i] );
+ len = Resolve_Read(&My_Connections[i].res_stat, readbuf, sizeof readbuf -1);
+ if (len == 0)
return;
- }
- len = (unsigned int) bytes_read;
- readbuf[len] = '\0';
- if (!array_catb(&s->buffer, readbuf, len)) {
- Log( LOG_CRIT, "Resolver: Can't append result %s to buffer: %s", readbuf, strerror( errno ));
- FreeRes_stat(&My_Connections[i]);
- return;
- }
- if (!array_cat0_temporary(&s->buffer)) {
- Log( LOG_CRIT, "Resolver: Can't append result %s to buffer: %s", readbuf, strerror( errno ));
- FreeRes_stat(&My_Connections[i]);
+ readbuf[len] = '\0';
+ identptr = strchr(readbuf, '\n');
+ assert(identptr != NULL);
+ if (!identptr) {
+ Log( LOG_CRIT, "Resolver: Got malformed result!");
return;
}
- /* If the result string is incomplete, return to main loop and
- * wait until we can read in more bytes. */
-#ifdef IDENTAUTH
-try_resolve:
-#endif
- bufptr = (char*) array_start(&s->buffer);
- assert(bufptr != NULL);
- ptr = strchr( bufptr, '\n' );
- if( ! ptr ) return;
- *ptr = '\0';
-
+ *identptr = '\0';
#ifdef DEBUG
- Log( LOG_DEBUG, "Got result from resolver: \"%s\" (%u bytes read), stage %d.", bufptr, len, s->stage);
+ Log( LOG_DEBUG, "Got result from resolver: \"%s\" (%u bytes read).", readbuf, len);
#endif
-
/* Okay, we got a complete result: this is a host name for outgoing
- * connections and a host name or IDENT user name (if enabled) for
+ * connections and a host name and IDENT user name (if enabled) for
* incoming connections.*/
- if( My_Connections[i].sock > NONE )
- {
+ if( My_Connections[i].sock > NONE ) {
/* Incoming connection. Search client ... */
c = Client_GetFromConn( i );
assert( c != NULL );
/* Only update client information of unregistered clients */
- if( Client_Type( c ) == CLIENT_UNKNOWN )
- {
- switch(s->stage) {
- case 0: /* host name */
- strlcpy( My_Connections[i].host, bufptr, sizeof( My_Connections[i].host));
-
- Client_SetHostname( c, bufptr);
+ if( Client_Type( c ) == CLIENT_UNKNOWN ) {
+ strlcpy(My_Connections[i].host, readbuf, sizeof( My_Connections[i].host));
+ Client_SetHostname( c, readbuf);
#ifdef IDENTAUTH
- /* clean up buffer for IDENT result */
- len = strlen(bufptr) + 1;
- assert(len <= array_bytes(&s->buffer));
- array_moveleft(&s->buffer, 1, len);
-
- /* Don't close pipe and clean up, but
- * instead wait for IDENT result */
- s->stage = 1;
- goto try_resolve;
-
- case 1: /* IDENT user name */
- if (array_bytes(&s->buffer)) {
- bufptr = (char*) array_start(&s->buffer);
- Log( LOG_INFO, "IDENT lookup for connection %ld: \"%s\".", i, bufptr);
- Client_SetUser( c, bufptr, true );
- }
- else Log( LOG_INFO, "IDENT lookup for connection %ld: no result.", i );
-#endif
- break;
- default:
- Log( LOG_ERR, "Resolver: got result for unknown stage %d!?", s->stage );
+ ++identptr;
+ if (*identptr) {
+ Log( LOG_INFO, "IDENT lookup for connection %ld: \"%s\".", i, identptr);
+ Client_SetUser( c, identptr, true );
+ } else {
+ Log( LOG_INFO, "IDENT lookup for connection %ld: no result.", i );
}
+#endif
}
#ifdef DEBUG
else Log( LOG_DEBUG, "Resolver: discarding result for already registered connection %d.", i );
#endif
- }
- else
- {
+ } else {
/* Outgoing connection (server link): set the IP address
* so that we can connect to it in the main loop. */
n = Conf_GetServer( i );
assert( n > NONE );
- bufptr = (char*) array_start(&s->buffer);
- strlcpy( Conf_Server[n].ip, bufptr, sizeof( Conf_Server[n].ip ));
+ strlcpy( Conf_Server[n].ip, readbuf, sizeof( Conf_Server[n].ip ));
}
- /* Clean up ... */
- FreeRes_stat( &My_Connections[i] );
-
/* Reset penalty time */
Conn_ResetPenalty( i );
-} /* Read_Resolver_Result */
+} /* cb_Read_Resolver_Result */
static void
int i, cnt;
cnt = 0;
- for( i = 0; i < Pool_Size; i++ )
- {
+ for( i = 0; i < Pool_Size; i++ ) {
if(( My_Connections[i].sock > NONE ) && ( My_Connections[i].addr.sin_addr.s_addr == addr_in.sin_addr.s_addr )) cnt++;
}
return cnt;