/[cvs]/eggdrop1.9/src/net.c
ViewVC logotype

Annotation of /eggdrop1.9/src/net.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.30 - (hide annotations) (download) (as text)
Sun Jun 24 20:49:23 2001 UTC (18 years ago) by poptix
Branch: MAIN
Changes since 1.29: +39 -2 lines
File MIME type: text/x-chdr
florian's 'netstuff' patch.

1 fabian 1.11 /*
2 segfault 1.1 * net.c -- handles:
3 fabian 1.11 * all raw network i/o
4 segfault 1.1 *
5 poptix 1.30 * $Id: net.c,v 1.29 2001/02/25 07:33:35 guppy Exp $
6 fabian 1.11 */
7     /*
8 segfault 1.1 * This is hereby released into the public domain.
9     * Robey Pointer, robey@netcom.com
10     */
11    
12 fabian 1.16 #include <fcntl.h>
13 segfault 1.1 #include "main.h"
14     #include <limits.h>
15     #include <string.h>
16     #include <netdb.h>
17     #include <sys/socket.h>
18     #if HAVE_SYS_SELECT_H
19 fabian 1.16 # include <sys/select.h>
20 segfault 1.1 #endif
21     #include <netinet/in.h>
22     #include <arpa/inet.h> /* is this really necessary? */
23     #include <errno.h>
24     #if HAVE_UNISTD_H
25 fabian 1.16 # include <unistd.h>
26 segfault 1.1 #endif
27     #include <setjmp.h>
28    
29     #if !HAVE_GETDTABLESIZE
30 fabian 1.16 # ifdef FD_SETSIZE
31     # define getdtablesize() FD_SETSIZE
32     # else
33     # define getdtablesize() 200
34     # endif
35 segfault 1.1 #endif
36    
37 fabian 1.16 extern struct dcc_t *dcc;
38 guppy 1.28 extern int backgrd, use_stderr, resolve_timeout, dcc_total;
39     extern unsigned long otraffic_irc_today, otraffic_bn_today,
40 fabian 1.16 otraffic_dcc_today, otraffic_filesys_today,
41     otraffic_trans_today, otraffic_unknown_today;
42    
43     char hostname[121] = ""; /* Hostname can be specified in the config
44     file */
45     char myip[121] = ""; /* IP can be specified in the config file */
46     char firewall[121] = ""; /* Socks server for firewall */
47     int firewallport = 1080; /* Default port of Sock4/5 firewalls */
48     char botuser[21] = "eggdrop"; /* Username of the user running the bot */
49     int dcc_sanitycheck = 0; /* We should do some sanity checking on dcc
50     connections. */
51     sock_list *socklist = NULL; /* Enough to be safe */
52     int MAXSOCKS = 0;
53     jmp_buf alarmret; /* Env buffer for alarm() returns */
54 segfault 1.1
55 fabian 1.16 /* Types of proxy */
56 segfault 1.1 #define PROXY_SOCKS 1
57     #define PROXY_SUN 2
58    
59    
60 fabian 1.16 /* I need an UNSIGNED long for dcc type stuff
61     */
62 segfault 1.1 IP my_atoul(char *s)
63     {
64     IP ret = 0;
65    
66     while ((*s >= '0') && (*s <= '9')) {
67     ret *= 10;
68     ret += ((*s) - '0');
69     s++;
70     }
71     return ret;
72     }
73    
74 fabian 1.16 /* Initialize the socklist
75     */
76 segfault 1.1 void init_net()
77     {
78     int i;
79    
80     for (i = 0; i < MAXSOCKS; i++) {
81     socklist[i].flags = SOCK_UNUSED;
82     }
83     }
84    
85     int expmem_net()
86     {
87     int i, tot = 0;
88    
89     for (i = 0; i < MAXSOCKS; i++) {
90     if (!(socklist[i].flags & SOCK_UNUSED)) {
91     if (socklist[i].inbuf != NULL)
92     tot += strlen(socklist[i].inbuf) + 1;
93     if (socklist[i].outbuf != NULL)
94     tot += socklist[i].outbuflen;
95     }
96     }
97     return tot;
98     }
99    
100 fabian 1.16 /* Get my ip number
101     */
102 segfault 1.1 IP getmyip()
103     {
104     struct hostent *hp;
105     char s[121];
106     IP ip;
107     struct in_addr *in;
108    
109 fabian 1.16 /* Could be pre-defined */
110 segfault 1.1 if (myip[0]) {
111     if ((myip[strlen(myip) - 1] >= '0') && (myip[strlen(myip) - 1] <= '9'))
112     return (IP) inet_addr(myip);
113     }
114 fabian 1.16 /* Also could be pre-defined */
115 segfault 1.1 if (hostname[0])
116     hp = gethostbyname(hostname);
117     else {
118     gethostname(s, 120);
119     hp = gethostbyname(s);
120     }
121     if (hp == NULL)
122     fatal("Hostname self-lookup failed.", 0);
123     in = (struct in_addr *) (hp->h_addr_list[0]);
124     ip = (IP) (in->s_addr);
125     return ip;
126     }
127    
128     void neterror(char *s)
129     {
130     switch (errno) {
131     case EADDRINUSE:
132     strcpy(s, "Address already in use");
133     break;
134     case EADDRNOTAVAIL:
135     strcpy(s, "Address invalid on remote machine");
136     break;
137     case EAFNOSUPPORT:
138     strcpy(s, "Address family not supported");
139     break;
140     case EALREADY:
141     strcpy(s, "Socket already in use");
142     break;
143     case EBADF:
144     strcpy(s, "Socket descriptor is bad");
145     break;
146     case ECONNREFUSED:
147     strcpy(s, "Connection refused");
148     break;
149     case EFAULT:
150     strcpy(s, "Namespace segment violation");
151     break;
152     case EINPROGRESS:
153     strcpy(s, "Operation in progress");
154     break;
155     case EINTR:
156     strcpy(s, "Timeout");
157     break;
158     case EINVAL:
159     strcpy(s, "Invalid namespace");
160     break;
161     case EISCONN:
162     strcpy(s, "Socket already connected");
163     break;
164     case ENETUNREACH:
165     strcpy(s, "Network unreachable");
166     break;
167     case ENOTSOCK:
168     strcpy(s, "File descriptor, not a socket");
169     break;
170     case ETIMEDOUT:
171     strcpy(s, "Connection timed out");
172     break;
173     case ENOTCONN:
174     strcpy(s, "Socket is not connected");
175     break;
176     case EHOSTUNREACH:
177     strcpy(s, "Host is unreachable");
178     break;
179     case EPIPE:
180     strcpy(s, "Broken pipe");
181     break;
182     #ifdef ECONNRESET
183     case ECONNRESET:
184     strcpy(s, "Connection reset by peer");
185     break;
186     #endif
187     #ifdef EACCES
188     case EACCES:
189     strcpy(s, "Permission denied");
190     break;
191     #endif
192     case 0:
193     strcpy(s, "Error 0");
194     break;
195     default:
196     sprintf(s, "Unforseen error %d", errno);
197     break;
198     }
199     }
200    
201 fabian 1.25 /* Sets/Unsets options for a specific socket.
202     *
203     * Returns: 0 - on success
204     * -1 - socket not found
205     * -2 - illegal operation
206     */
207     int sockoptions(int sock, int operation, int sock_options)
208     {
209     int i;
210    
211     for (i = 0; i < MAXSOCKS; i++)
212 poptix 1.30 if ((socklist[i].sock == sock) && !(socklist[i].flags & SOCK_UNUSED)) {
213 fabian 1.25 if (operation == EGG_OPTION_SET)
214     socklist[i].flags |= sock_options;
215     else if (operation == EGG_OPTION_UNSET)
216     socklist[i].flags &= ~sock_options;
217     else
218     return -2;
219     return 0;
220     }
221     return -1;
222     }
223    
224 fabian 1.16 /* Return a free entry in the socket entry
225     */
226 fabian 1.3 int allocsock(int sock, int options)
227 segfault 1.1 {
228     int i;
229    
230     for (i = 0; i < MAXSOCKS; i++) {
231     if (socklist[i].flags & SOCK_UNUSED) {
232     /* yay! there is table space */
233     socklist[i].inbuf = socklist[i].outbuf = NULL;
234 fabian 1.25 socklist[i].inbuflen = socklist[i].outbuflen = 0;
235 segfault 1.1 socklist[i].flags = options;
236     socklist[i].sock = sock;
237 fabian 1.3 return i;
238     }
239     }
240     fatal("Socket table is full!", 0);
241 fabian 1.16 return -1; /* Never reached */
242 fabian 1.3 }
243    
244 fabian 1.16 /* Request a normal socket for i/o
245     */
246 fabian 1.3 void setsock(int sock, int options)
247     {
248     int i = allocsock(sock, options);
249     int parm;
250    
251 fabian 1.16 if (((sock != STDOUT) || backgrd) &&
252     !(socklist[i].flags & SOCK_NONSOCK)) {
253     parm = 1;
254 fabian 1.3 setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &parm, sizeof(int));
255 segfault 1.1
256 fabian 1.16 parm = 0;
257     setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &parm, sizeof(int));
258     }
259     if (options & SOCK_LISTEN) {
260     /* Tris says this lets us grab the same port again next time */
261     parm = 1;
262     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &parm, sizeof(int));
263     }
264     /* Yay async i/o ! */
265     fcntl(sock, F_SETFL, O_NONBLOCK);
266 segfault 1.1 }
267    
268     int getsock(int options)
269     {
270     int sock = socket(AF_INET, SOCK_STREAM, 0);
271    
272 fabian 1.24 if (sock >= 0)
273     setsock(sock, options);
274     else
275     putlog(LOG_MISC, "*", "Warning: Can't create new socket!");
276 segfault 1.1 return sock;
277     }
278    
279 fabian 1.16 /* Done with a socket
280     */
281 fabian 1.24 void killsock(register int sock)
282 segfault 1.1 {
283 fabian 1.24 register int i;
284 segfault 1.1
285 fabian 1.24 /* Ignore invalid sockets. */
286     if (sock < 0)
287     return;
288 segfault 1.1 for (i = 0; i < MAXSOCKS; i++) {
289 fabian 1.2 if ((socklist[i].sock == sock) && !(socklist[i].flags & SOCK_UNUSED)) {
290 segfault 1.1 close(socklist[i].sock);
291     if (socklist[i].inbuf != NULL) {
292     nfree(socklist[i].inbuf);
293     socklist[i].inbuf = NULL;
294     }
295     if (socklist[i].outbuf != NULL) {
296     nfree(socklist[i].outbuf);
297     socklist[i].outbuf = NULL;
298     socklist[i].outbuflen = 0;
299     }
300     socklist[i].flags = SOCK_UNUSED;
301     return;
302     }
303     }
304     putlog(LOG_MISC, "*", "Attempt to kill un-allocated socket %d !!", sock);
305     }
306    
307 fabian 1.16 /* Send connection request to proxy
308     */
309 segfault 1.1 static int proxy_connect(int sock, char *host, int port, int proxy)
310     {
311     unsigned char x[10];
312     struct hostent *hp;
313 fabian 1.26 char s[256];
314 segfault 1.1 int i;
315    
316     /* socks proxy */
317     if (proxy == PROXY_SOCKS) {
318     /* numeric IP? */
319 fabian 1.26 if (host[strlen(host) - 1] >= '0' && host[strlen(host) - 1] <= '9') {
320 segfault 1.1 IP ip = ((IP) inet_addr(host)); /* drummer */
321 fabian 1.18 egg_memcpy(x, &ip, 4); /* Beige@Efnet */
322 segfault 1.1 } else {
323     /* no, must be host.domain */
324     if (!setjmp(alarmret)) {
325     alarm(resolve_timeout);
326     hp = gethostbyname(host);
327     alarm(0);
328     } else {
329     hp = NULL;
330     }
331     if (hp == NULL) {
332     killsock(sock);
333     return -2;
334     }
335 fabian 1.18 egg_memcpy(x, hp->h_addr, hp->h_length);
336 segfault 1.1 }
337 fabian 1.26 for (i = 0; i < MAXSOCKS; i++)
338     if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock)
339 segfault 1.1 socklist[i].flags |= SOCK_PROXYWAIT; /* drummer */
340 fabian 1.26 egg_snprintf(s, sizeof s, "\004\001%c%c%c%c%c%c%s", (port >> 8) % 256,
341     (port % 256), x[0], x[1], x[2], x[3], botuser);
342 segfault 1.1 tputs(sock, s, strlen(botuser) + 9); /* drummer */
343     } else if (proxy == PROXY_SUN) {
344 fabian 1.26 egg_snprintf(s, sizeof s, "%s %d\n", host, port);
345 segfault 1.1 tputs(sock, s, strlen(s)); /* drummer */
346     }
347     return sock;
348     }
349    
350 fabian 1.16 /* Starts a connection attempt to a socket
351 fabian 1.11 *
352 fabian 1.3 * If given a normal hostname, this will be resolved to the corresponding
353     * IP address first. PLEASE try to use the non-blocking dns functions
354     * instead and then call this function with the IP address to avoid blocking.
355 fabian 1.11 *
356 segfault 1.1 * returns <0 if connection refused:
357     * -1 neterror() type error
358 fabian 1.16 * -2 can't resolve hostname
359     */
360 segfault 1.1 int open_telnet_raw(int sock, char *server, int sport)
361     {
362     struct sockaddr_in name;
363     struct hostent *hp;
364     char host[121];
365     int i, port;
366     volatile int proxy;
367    
368     /* firewall? use socks */
369     if (firewall[0]) {
370     if (firewall[0] == '!') {
371     proxy = PROXY_SUN;
372     strcpy(host, &firewall[1]);
373     } else {
374     proxy = PROXY_SOCKS;
375     strcpy(host, firewall);
376     }
377     port = firewallport;
378     } else {
379     proxy = 0;
380     strcpy(host, server);
381     port = sport;
382     }
383     /* patch by tris for multi-hosted machines: */
384 fabian 1.18 egg_bzero((char *) &name, sizeof(struct sockaddr_in));
385 segfault 1.1
386     name.sin_family = AF_INET;
387     name.sin_addr.s_addr = (myip[0] ? getmyip() : INADDR_ANY);
388 fabian 1.4 if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0)
389 segfault 1.1 return -1;
390 fabian 1.18 egg_bzero((char *) &name, sizeof(struct sockaddr_in));
391 segfault 1.1
392     name.sin_family = AF_INET;
393 fabian 1.21 name.sin_port = htons(port);
394 fabian 1.16 /* Numeric IP? */
395 segfault 1.1 if ((host[strlen(host) - 1] >= '0') && (host[strlen(host) - 1] <= '9'))
396     name.sin_addr.s_addr = inet_addr(host);
397     else {
398 fabian 1.16 /* No, must be host.domain */
399     debug0("WARNING: open_telnet_raw() is about to block in gethostbyname()!");
400 segfault 1.1 if (!setjmp(alarmret)) {
401     alarm(resolve_timeout);
402     hp = gethostbyname(host);
403     alarm(0);
404     } else {
405     hp = NULL;
406     }
407 fabian 1.4 if (hp == NULL)
408 segfault 1.1 return -2;
409 fabian 1.18 egg_memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
410 segfault 1.1 name.sin_family = hp->h_addrtype;
411     }
412     for (i = 0; i < MAXSOCKS; i++) {
413     if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == sock))
414 fabian 1.17 socklist[i].flags = (socklist[i].flags & ~SOCK_VIRTUAL) | SOCK_CONNECT;
415 segfault 1.1 }
416     if (connect(sock, (struct sockaddr *) &name,
417     sizeof(struct sockaddr_in)) < 0) {
418     if (errno == EINPROGRESS) {
419 fabian 1.16 /* Firewall? announce connect attempt to proxy */
420 segfault 1.1 if (firewall[0])
421     return proxy_connect(sock, server, sport, proxy);
422     return sock; /* async success! */
423 fabian 1.4 } else
424 segfault 1.1 return -1;
425     }
426 fabian 1.16 /* Synchronous? :/ */
427 segfault 1.1 if (firewall[0])
428     return proxy_connect(sock, server, sport, proxy);
429     return sock;
430     }
431    
432 fabian 1.16 /* Ordinary non-binary connection attempt */
433 segfault 1.1 int open_telnet(char *server, int port)
434     {
435 fabian 1.4 int sock = getsock(0),
436     ret = open_telnet_raw(sock, server, port);
437    
438     if (ret < 0)
439     killsock(sock);
440     return ret;
441 segfault 1.1 }
442    
443 fabian 1.16 /* Returns a socket number for a listening socket that will accept any
444 fabian 1.19 * connection on a certain address -- port # is returned in port
445 fabian 1.16 */
446 fabian 1.19 int open_address_listen(IP addr, int *port)
447 segfault 1.1 {
448 fabian 1.13 int sock;
449     unsigned int addrlen;
450 segfault 1.1 struct sockaddr_in name;
451    
452     if (firewall[0]) {
453     /* FIXME: can't do listen port thru firewall yet */
454 fabian 1.6 putlog(LOG_MISC, "*", "!! Cant open a listen port (you are using a firewall)");
455 segfault 1.1 return -1;
456     }
457 fabian 1.24
458 segfault 1.1 sock = getsock(SOCK_LISTEN);
459 fabian 1.24 if (sock < 1)
460     return -1;
461    
462 fabian 1.18 egg_bzero((char *) &name, sizeof(struct sockaddr_in));
463 segfault 1.1 name.sin_family = AF_INET;
464 fabian 1.19 name.sin_port = htons(*port); /* 0 = just assign us a port */
465     name.sin_addr.s_addr = addr;
466 segfault 1.1 if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
467     killsock(sock);
468 guppy 1.29 if (errno == EADDRNOTAVAIL || errno == EAFNOSUPPORT)
469     return -2;
470     else
471     return -1;
472 segfault 1.1 }
473     /* what port are we on? */
474     addrlen = sizeof(name);
475     if (getsockname(sock, (struct sockaddr *) &name, &addrlen) < 0) {
476     killsock(sock);
477     return -1;
478     }
479 fabian 1.19 *port = ntohs(name.sin_port);
480 segfault 1.1 if (listen(sock, 1) < 0) {
481     killsock(sock);
482     return -1;
483     }
484     return sock;
485 fabian 1.19 }
486    
487     /* Returns a socket number for a listening socket that will accept any
488     * connection -- port # is returned in port
489     */
490     inline int open_listen(int *port)
491     {
492     return open_address_listen(myip[0] ? getmyip() : INADDR_ANY, port);
493 segfault 1.1 }
494    
495 fabian 1.3 /* Given a network-style IP address, returns the hostname. The hostname
496     * will be in the "##.##.##.##" format if there was an error.
497     *
498     * NOTE: This function is depreciated. Try using the async dns approach
499 fabian 1.16 * instead.
500     */
501 segfault 1.1 char *hostnamefromip(unsigned long ip)
502     {
503     struct hostent *hp;
504     unsigned long addr = ip;
505     unsigned char *p;
506 fabian 1.3 static char s[UHOSTLEN];
507 segfault 1.1
508     if (!setjmp(alarmret)) {
509     alarm(resolve_timeout);
510     hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET);
511     alarm(0);
512     } else {
513     hp = NULL;
514     }
515     if (hp == NULL) {
516     p = (unsigned char *) &addr;
517     sprintf(s, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
518     return s;
519     }
520 guppy 1.27 strncpyz(s, hp->h_name, sizeof s);
521 segfault 1.1 return s;
522     }
523    
524 fabian 1.5 /* Returns the given network byte order IP address in the
525 fabian 1.16 * dotted format - "##.##.##.##"
526     */
527 fabian 1.3 char *iptostr(IP ip)
528     {
529     struct in_addr a;
530    
531     a.s_addr = ip;
532     return inet_ntoa(a);
533     }
534    
535 fabian 1.16 /* Short routine to answer a connect received on a socket made previously
536 segfault 1.1 * by open_listen ... returns hostname of the caller & the new socket
537 fabian 1.16 * does NOT dispose of old "public" socket!
538     */
539     int answer(int sock, char *caller, unsigned long *ip, unsigned short *port,
540     int binary)
541 segfault 1.1 {
542 fabian 1.13 int new_sock;
543     unsigned int addrlen;
544 segfault 1.1 struct sockaddr_in from;
545 fabian 1.25
546 fabian 1.8 addrlen = sizeof(struct sockaddr);
547 segfault 1.1 new_sock = accept(sock, (struct sockaddr *) &from, &addrlen);
548     if (new_sock < 0)
549     return -1;
550     if (ip != NULL) {
551     *ip = from.sin_addr.s_addr;
552 fabian 1.16 /* This is now done asynchronously. We now only provide the IP address.
553     *
554 fabian 1.3 * strncpy(caller, hostnamefromip(*ip), 120);
555     */
556 guppy 1.27 strncpyz(caller, iptostr(*ip), 121);
557 fabian 1.21 *ip = ntohl(*ip);
558 segfault 1.1 }
559     if (port != NULL)
560 fabian 1.21 *port = ntohs(from.sin_port);
561 fabian 1.16 /* Set up all the normal socket crap */
562 segfault 1.1 setsock(new_sock, (binary ? SOCK_BINARY : 0));
563     return new_sock;
564     }
565    
566 fabian 1.16 /* Like open_telnet, but uses server & port specifications of dcc
567     */
568 segfault 1.1 int open_telnet_dcc(int sock, char *server, char *port)
569     {
570     int p;
571     unsigned long addr;
572     char sv[121];
573     unsigned char c[4];
574    
575     if (port != NULL)
576     p = atoi(port);
577     else
578     p = 2000;
579     if (server != NULL)
580     addr = my_atoul(server);
581     else
582     addr = 0L;
583     if (addr < (1 << 24))
584     return -3; /* fake address */
585     c[0] = (addr >> 24) & 0xff;
586     c[1] = (addr >> 16) & 0xff;
587     c[2] = (addr >> 8) & 0xff;
588     c[3] = addr & 0xff;
589     sprintf(sv, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]);
590     /* strcpy(sv,hostnamefromip(addr)); */
591     p = open_telnet_raw(sock, sv, p);
592     return p;
593     }
594    
595 fabian 1.16 /* Attempts to read from all the sockets in socklist
596 segfault 1.1 * fills s with up to 511 bytes if available, and returns the array index
597 fabian 1.16 *
598     * on EOF: returns -1, with socket in len
599     * on socket error: returns -2
600     * if nothing is ready: returns -3
601     */
602 segfault 1.1 static int sockread(char *s, int *len)
603     {
604     fd_set fd;
605     int fds, i, x;
606     struct timeval t;
607     int grab = 511;
608    
609     fds = getdtablesize();
610     #ifdef FD_SETSIZE
611     if (fds > FD_SETSIZE)
612 fabian 1.16 fds = FD_SETSIZE; /* Fixes YET ANOTHER freebsd bug!!! */
613 segfault 1.1 #endif
614     /* timeout: 1 sec */
615     t.tv_sec = 1;
616     t.tv_usec = 0;
617     FD_ZERO(&fd);
618 fabian 1.17
619 segfault 1.1 for (i = 0; i < MAXSOCKS; i++)
620 fabian 1.17 if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_VIRTUAL))) {
621 segfault 1.1 if ((socklist[i].sock == STDOUT) && !backgrd)
622     FD_SET(STDIN, &fd);
623     else
624     FD_SET(socklist[i].sock, &fd);
625     }
626     #ifdef HPUX_HACKS
627     #ifndef HPUX10_HACKS
628     x = select(fds, (int *) &fd, (int *) NULL, (int *) NULL, &t);
629     #else
630     x = select(fds, &fd, NULL, NULL, &t);
631     #endif
632     #else
633     x = select(fds, &fd, NULL, NULL, &t);
634     #endif
635     if (x > 0) {
636 fabian 1.16 /* Something happened */
637 segfault 1.1 for (i = 0; i < MAXSOCKS; i++) {
638     if ((!(socklist[i].flags & SOCK_UNUSED)) &&
639     ((FD_ISSET(socklist[i].sock, &fd)) ||
640     ((socklist[i].sock == STDOUT) && (!backgrd) &&
641     (FD_ISSET(STDIN, &fd))))) {
642     if (socklist[i].flags & (SOCK_LISTEN | SOCK_CONNECT)) {
643 fabian 1.16 /* Listening socket -- don't read, just return activity */
644     /* Same for connection attempt */
645 segfault 1.1 /* (for strong connections, require a read to succeed first) */
646     if (socklist[i].flags & SOCK_PROXYWAIT) { /* drummer */
647 fabian 1.16 /* Hang around to get the return code from proxy */
648 segfault 1.1 grab = 10;
649     } else if (!(socklist[i].flags & SOCK_STRONGCONN)) {
650     debug1("net: connect! sock %d", socklist[i].sock);
651     s[0] = 0;
652     *len = 0;
653     return i;
654     }
655 fabian 1.3 } else if (socklist[i].flags & SOCK_PASS) {
656     s[0] = 0;
657     *len = 0;
658     return i;
659 segfault 1.1 }
660     if ((socklist[i].sock == STDOUT) && !backgrd)
661     x = read(STDIN, s, grab);
662     else
663     x = read(socklist[i].sock, s, grab);
664     if (x <= 0) { /* eof */
665     if (x == EAGAIN) {
666     s[0] = 0;
667     *len = 0;
668     return -3;
669     }
670     *len = socklist[i].sock;
671     socklist[i].flags &= ~SOCK_CONNECT;
672     debug1("net: eof!(read) socket %d", socklist[i].sock);
673     return -1;
674     }
675     s[x] = 0;
676     *len = x;
677     if (socklist[i].flags & SOCK_PROXYWAIT) {
678     debug2("net: socket: %d proxy errno: %d", socklist[i].sock, s[1]);
679     socklist[i].flags &= ~(SOCK_CONNECT | SOCK_PROXYWAIT);
680     switch (s[1]) {
681 fabian 1.16 case 90: /* Success */
682 segfault 1.1 s[0] = 0;
683     *len = 0;
684     return i;
685 fabian 1.16 case 91: /* Failed */
686 segfault 1.1 errno = ECONNREFUSED;
687     break;
688 fabian 1.16 case 92: /* No identd */
689     case 93: /* Identd said wrong username */
690     /* A better error message would be "socks misconfigured"
691     * or "identd not working" but this is simplest.
692     */
693 segfault 1.1 errno = ENETUNREACH;
694     break;
695     }
696     *len = socklist[i].sock;
697     return -1;
698     }
699     return i;
700     }
701     }
702     } else if (x == -1)
703     return -2; /* socket error */
704     else {
705     s[0] = 0;
706     *len = 0;
707     }
708     return -3;
709     }
710    
711     /* sockgets: buffer and read from sockets
712     *
713 fabian 1.16 * Attempts to read from all registered sockets for up to one second. if
714 segfault 1.1 * after one second, no complete data has been received from any of the
715     * sockets, 's' will be empty, 'len' will be 0, and sockgets will return -3.
716     * if there is returnable data received from a socket, the data will be
717     * in 's' (null-terminated if non-binary), the length will be returned
718     * in len, and the socket number will be returned.
719     * normal sockets have their input buffered, and each call to sockgets
720     * will return one line terminated with a '\n'. binary sockets are not
721     * buffered and return whatever coems in as soon as it arrives.
722     * listening sockets will return an empty string when a connection comes in.
723     * connecting sockets will return an empty string on a successful connect,
724     * or EOF on a failed connect.
725     * if an EOF is detected from any of the sockets, that socket number will be
726     * put in len, and -1 will be returned.
727     * the maximum length of the string returned is 512 (including null)
728 fabian 1.25 *
729     * Returns -4 if we handled something that shouldn't be handled by the
730     * dcc functions. Simply ignore it.
731 segfault 1.1 */
732    
733     int sockgets(char *s, int *len)
734     {
735     char xx[514], *p, *px;
736     int ret, i, data = 0;
737    
738     for (i = 0; i < MAXSOCKS; i++) {
739 fabian 1.16 /* Check for stored-up data waiting to be processed */
740 fabian 1.25 if (!(socklist[i].flags & SOCK_UNUSED) &&
741     !(socklist[i].flags & SOCK_BUFFER) && (socklist[i].inbuf != NULL)) {
742     if (!(socklist[i].flags & SOCK_BINARY)) {
743     /* look for \r too cos windows can't follow RFCs */
744     p = strchr(socklist[i].inbuf, '\n');
745     if (p == NULL)
746     p = strchr(socklist[i].inbuf, '\r');
747     if (p != NULL) {
748     *p = 0;
749     if (strlen(socklist[i].inbuf) > 510)
750     socklist[i].inbuf[510] = 0;
751     strcpy(s, socklist[i].inbuf);
752     px = (char *) nmalloc(strlen(p + 1) + 1);
753     strcpy(px, p + 1);
754     nfree(socklist[i].inbuf);
755     if (px[0])
756     socklist[i].inbuf = px;
757     else {
758     nfree(px);
759     socklist[i].inbuf = NULL;
760     }
761     /* Strip CR if this was CR/LF combo */
762     if (s[strlen(s) - 1] == '\r')
763     s[strlen(s) - 1] = 0;
764     *len = strlen(s);
765     return socklist[i].sock;
766     }
767     } else {
768     /* Handling buffered binary data (must have been SOCK_BUFFER before). */
769     if (socklist[i].inbuflen <= 510) {
770     *len = socklist[i].inbuflen;
771     egg_memcpy(s, socklist[i].inbuf, socklist[i].inbuflen);
772     nfree(socklist[i].inbuf);
773 poptix 1.30 socklist[i].inbuf = NULL;
774 fabian 1.25 socklist[i].inbuflen = 0;
775     } else {
776     /* Split up into chunks of 510 bytes. */
777     *len = 510;
778     egg_memcpy(s, socklist[i].inbuf, *len);
779     egg_memcpy(socklist[i].inbuf, socklist[i].inbuf + *len, *len);
780     socklist[i].inbuflen -= *len;
781     socklist[i].inbuf = nrealloc(socklist[i].inbuf,
782     socklist[i].inbuflen);
783 segfault 1.1 }
784     return socklist[i].sock;
785     }
786     }
787 fabian 1.16 /* Also check any sockets that might have EOF'd during write */
788 segfault 1.1 if (!(socklist[i].flags & SOCK_UNUSED)
789     && (socklist[i].flags & SOCK_EOFD)) {
790     s[0] = 0;
791     *len = socklist[i].sock;
792     return -1;
793     }
794     }
795 fabian 1.16 /* No pent-up data of any worth -- down to business */
796 segfault 1.1 *len = 0;
797     ret = sockread(xx, len);
798     if (ret < 0) {
799     s[0] = 0;
800     return ret;
801     }
802 fabian 1.16 /* Binary, listening and passed on sockets don't get buffered. */
803 segfault 1.1 if (socklist[ret].flags & SOCK_CONNECT) {
804     if (socklist[ret].flags & SOCK_STRONGCONN) {
805     socklist[ret].flags &= ~SOCK_STRONGCONN;
806 fabian 1.25 /* Buffer any data that came in, for future read. */
807     socklist[ret].inbuflen = *len;
808     socklist[ret].inbuf = (char *) nmalloc(*len + 1);
809     /* It might be binary data. You never know. */
810     egg_memcpy(socklist[ret].inbuf, xx, *len);
811     socklist[ret].inbuf[*len] = 0;
812 segfault 1.1 }
813     socklist[ret].flags &= ~SOCK_CONNECT;
814     s[0] = 0;
815     return socklist[ret].sock;
816     }
817     if (socklist[ret].flags & SOCK_BINARY) {
818 fabian 1.18 egg_memcpy(s, xx, *len);
819 segfault 1.1 return socklist[ret].sock;
820     }
821 fabian 1.3 if ((socklist[ret].flags & SOCK_LISTEN) ||
822     (socklist[ret].flags & SOCK_PASS))
823 segfault 1.1 return socklist[ret].sock;
824 fabian 1.25 if (socklist[ret].flags & SOCK_BUFFER) {
825     socklist[ret].inbuf = (char *) nrealloc(socklist[ret].inbuf,
826     socklist[ret].inbuflen + *len + 1);
827     egg_memcpy(socklist[ret].inbuf + socklist[ret].inbuflen, xx, *len);
828     socklist[ret].inbuflen += *len;
829     /* We don't know whether it's binary data. Make sure normal strings
830     will be handled properly later on too. */
831     socklist[ret].inbuf[socklist[ret].inbuflen] = 0;
832     return -4; /* Ignore this one. */
833     }
834 fabian 1.16 /* Might be necessary to prepend stored-up data! */
835 segfault 1.1 if (socklist[ret].inbuf != NULL) {
836     p = socklist[ret].inbuf;
837     socklist[ret].inbuf = (char *) nmalloc(strlen(p) + strlen(xx) + 1);
838     strcpy(socklist[ret].inbuf, p);
839     strcat(socklist[ret].inbuf, xx);
840     nfree(p);
841     if (strlen(socklist[ret].inbuf) < 512) {
842     strcpy(xx, socklist[ret].inbuf);
843     nfree(socklist[ret].inbuf);
844     socklist[ret].inbuf = NULL;
845 fabian 1.25 socklist[ret].inbuflen = 0;
846 segfault 1.1 } else {
847     p = socklist[ret].inbuf;
848 fabian 1.25 socklist[ret].inbuflen = strlen(p) - 510;
849     socklist[ret].inbuf = (char *) nmalloc(socklist[ret].inbuflen + 1);
850 segfault 1.1 strcpy(socklist[ret].inbuf, p + 510);
851     *(p + 510) = 0;
852     strcpy(xx, p);
853     nfree(p);
854     /* (leave the rest to be post-pended later) */
855     }
856     }
857 fabian 1.16 /* Look for EOL marker; if it's there, i have something to show */
858 segfault 1.1 p = strchr(xx, '\n');
859     if (p == NULL)
860     p = strchr(xx, '\r');
861     if (p != NULL) {
862     *p = 0;
863     strcpy(s, xx);
864     strcpy(xx, p + 1);
865     if (s[strlen(s) - 1] == '\r')
866     s[strlen(s) - 1] = 0;
867     data = 1; /* DCC_CHAT may now need to process a
868 fabian 1.16 blank line */
869 segfault 1.1 /* NO! */
870     /* if (!s[0]) strcpy(s," "); */
871     } else {
872     s[0] = 0;
873     if (strlen(xx) >= 510) {
874 fabian 1.16 /* String is too long, so just insert fake \n */
875 segfault 1.1 strcpy(s, xx);
876     xx[0] = 0;
877     data = 1;
878     }
879     }
880     *len = strlen(s);
881 fabian 1.16 /* Anything left that needs to be saved? */
882 segfault 1.1 if (!xx[0]) {
883     if (data)
884     return socklist[ret].sock;
885     else
886     return -3;
887     }
888 fabian 1.16 /* Prepend old data back */
889 segfault 1.1 if (socklist[ret].inbuf != NULL) {
890     p = socklist[ret].inbuf;
891 fabian 1.25 socklist[ret].inbuflen = strlen(p) + strlen(xx);
892     socklist[ret].inbuf = (char *) nmalloc(socklist[ret].inbuflen + 1);
893 segfault 1.1 strcpy(socklist[ret].inbuf, xx);
894     strcat(socklist[ret].inbuf, p);
895     nfree(p);
896     } else {
897 fabian 1.25 socklist[ret].inbuflen = strlen(xx);
898     socklist[ret].inbuf = (char *) nmalloc(socklist[ret].inbuflen + 1);
899 segfault 1.1 strcpy(socklist[ret].inbuf, xx);
900     }
901     if (data) {
902     return socklist[ret].sock;
903     } else {
904     return -3;
905     }
906     }
907    
908 fabian 1.16 /* Dump something to a socket
909     *
910     * NOTE: Do NOT put Contexts in here if you want DEBUG to be meaningful!!
911     */
912 fabian 1.12 void tputs(register int z, char *s, unsigned int len)
913 segfault 1.1 {
914 fabian 1.12 register int i, x, idx;
915 segfault 1.1 char *p;
916 fabian 1.12 static int inhere = 0;
917 segfault 1.1
918     if (z < 0)
919     return; /* um... HELLO?! sanity check please! */
920     if (((z == STDOUT) || (z == STDERR)) && (!backgrd || use_stderr)) {
921     write(z, s, len);
922     return;
923     }
924     for (i = 0; i < MAXSOCKS; i++) {
925     if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == z)) {
926 fabian 1.10 for (idx = 0; idx < dcc_total; idx++) {
927     if (dcc[idx].sock == z) {
928     if (dcc[idx].type) {
929     if (dcc[idx].type->name) {
930     if (!strncmp(dcc[idx].type->name, "BOT", 3)) {
931     otraffic_bn_today += len;
932     break;
933     } else if (!strcmp(dcc[idx].type->name, "SERVER")) {
934     otraffic_irc_today += len;
935     break;
936     } else if (!strncmp(dcc[idx].type->name, "CHAT", 4)) {
937     otraffic_dcc_today += len;
938     break;
939     } else if (!strncmp(dcc[idx].type->name, "FILES", 5)) {
940     otraffic_filesys_today += len;
941     break;
942     } else if (!strcmp(dcc[idx].type->name, "SEND")) {
943     otraffic_trans_today += len;
944     break;
945     } else if (!strncmp(dcc[idx].type->name, "GET", 3)) {
946     otraffic_trans_today += len;
947     break;
948     } else {
949     otraffic_unknown_today += len;
950     break;
951     }
952     }
953     }
954     }
955     }
956    
957 segfault 1.1 if (socklist[i].outbuf != NULL) {
958 fabian 1.16 /* Already queueing: just add it */
959 segfault 1.1 p = (char *) nrealloc(socklist[i].outbuf, socklist[i].outbuflen + len);
960 fabian 1.18 egg_memcpy(p + socklist[i].outbuflen, s, len);
961 segfault 1.1 socklist[i].outbuf = p;
962     socklist[i].outbuflen += len;
963     return;
964     }
965 fabian 1.16 /* Try. */
966 segfault 1.1 x = write(z, s, len);
967     if (x == (-1))
968     x = 0;
969     if (x < len) {
970 fabian 1.16 /* Socket is full, queue it */
971 segfault 1.1 socklist[i].outbuf = (char *) nmalloc(len - x);
972 fabian 1.18 egg_memcpy(socklist[i].outbuf, &s[x], len - x);
973 segfault 1.1 socklist[i].outbuflen = len - x;
974     }
975     return;
976     }
977     }
978 fabian 1.12 /* Make sure we don't cause a crash by looping here */
979     if (!inhere) {
980     inhere = 1;
981 fabian 1.16
982 fabian 1.12 putlog(LOG_MISC, "*", "!!! writing to nonexistent socket: %d", z);
983     s[strlen(s) - 1] = 0;
984     putlog(LOG_MISC, "*", "!-> '%s'", s);
985 fabian 1.16
986 fabian 1.12 inhere = 0;
987     }
988 segfault 1.1 }
989    
990     /* tputs might queue data for sockets, let's dump as much of it as
991 fabian 1.16 * possible.
992     */
993 segfault 1.1 void dequeue_sockets()
994     {
995     int i, x;
996    
997     for (i = 0; i < MAXSOCKS; i++) {
998     if (!(socklist[i].flags & SOCK_UNUSED) &&
999 fabian 1.22 socklist[i].outbuf != NULL) {
1000 fabian 1.16 /* Trick tputs into doing the work */
1001 segfault 1.1 x = write(socklist[i].sock, socklist[i].outbuf,
1002     socklist[i].outbuflen);
1003     if ((x < 0) && (errno != EAGAIN)
1004     #ifdef EBADSLT
1005     && (errno != EBADSLT)
1006     #endif
1007     #ifdef ENOTCONN
1008     && (errno != ENOTCONN)
1009     #endif
1010     ) {
1011 fabian 1.16 /* This detects an EOF during writing */
1012 segfault 1.1 debug3("net: eof!(write) socket %d (%s,%d)", socklist[i].sock,
1013     strerror(errno), errno);
1014     socklist[i].flags |= SOCK_EOFD;
1015     } else if (x == socklist[i].outbuflen) {
1016 fabian 1.16 /* If the whole buffer was sent, nuke it */
1017 segfault 1.1 nfree(socklist[i].outbuf);
1018     socklist[i].outbuf = NULL;
1019     socklist[i].outbuflen = 0;
1020     } else if (x > 0) {
1021     char *p = socklist[i].outbuf;
1022    
1023 fabian 1.16 /* This removes any sent bytes from the beginning of the buffer */
1024 segfault 1.1 socklist[i].outbuf = (char *) nmalloc(socklist[i].outbuflen - x);
1025 fabian 1.18 egg_memcpy(socklist[i].outbuf, p + x, socklist[i].outbuflen - x);
1026 segfault 1.1 socklist[i].outbuflen -= x;
1027     nfree(p);
1028     }
1029 fabian 1.15
1030     /* All queued data was sent. Call handler if one exists and the
1031     * dcc entry wants it.
1032     */
1033     if (!socklist[i].outbuf) {
1034     int idx = findanyidx(socklist[i].sock);
1035    
1036 fabian 1.22 if (idx > 0 && dcc[idx].type && dcc[idx].type->outdone)
1037 fabian 1.15 dcc[idx].type->outdone(idx);
1038     }
1039 segfault 1.1 }
1040     }
1041     }
1042 fabian 1.16
1043 segfault 1.1
1044 fabian 1.16 /*
1045     * Debugging stuff
1046     */
1047 segfault 1.1
1048     void tell_netdebug(int idx)
1049     {
1050     int i;
1051     char s[80];
1052    
1053     dprintf(idx, "Open sockets:");
1054     for (i = 0; i < MAXSOCKS; i++) {
1055     if (!(socklist[i].flags & SOCK_UNUSED)) {
1056     sprintf(s, " %d", socklist[i].sock);
1057     if (socklist[i].flags & SOCK_BINARY)
1058     strcat(s, " (binary)");
1059     if (socklist[i].flags & SOCK_LISTEN)
1060     strcat(s, " (listen)");
1061 fabian 1.3 if (socklist[i].flags & SOCK_PASS)
1062     strcat(s, " (passed on)");
1063 segfault 1.1 if (socklist[i].flags & SOCK_CONNECT)
1064     strcat(s, " (connecting)");
1065     if (socklist[i].flags & SOCK_STRONGCONN)
1066     strcat(s, " (strong)");
1067     if (socklist[i].flags & SOCK_NONSOCK)
1068     strcat(s, " (file)");
1069     if (socklist[i].inbuf != NULL)
1070     sprintf(&s[strlen(s)], " (inbuf: %04X)", strlen(socklist[i].inbuf));
1071     if (socklist[i].outbuf != NULL)
1072     sprintf(&s[strlen(s)], " (outbuf: %06lX)", socklist[i].outbuflen);
1073     strcat(s, ",");
1074     dprintf(idx, "%s", s);
1075     }
1076     }
1077     dprintf(idx, " done.\n");
1078     }
1079    
1080     /* Security-flavoured sanity checking on DCC connections of all sorts can be
1081     * done with this routine. Feed it the proper information from your DCC
1082     * before you attempt the connection, and this will make an attempt at
1083     * figuring out if the connection is really that person, or someone screwing
1084     * around. It's not foolproof, but anything that fails this check probably
1085     * isn't going to work anyway due to masquerading firewalls, NAT routers,
1086 fabian 1.16 * or bugs in mIRC.
1087     */
1088 segfault 1.1 int sanitycheck_dcc(char *nick, char *from, char *ipaddy, char *port)
1089     {
1090     /* According to the latest RFC, the clients SHOULD be able to handle
1091 fabian 1.16 * DNS names that are up to 255 characters long. This is not broken.
1092     */
1093 fabian 1.3 char badaddress[16];
1094 segfault 1.1 IP ip = my_atoul(ipaddy);
1095     int prt = atoi(port);
1096    
1097     /* It is disabled HERE so we only have to check in *one* spot! */
1098     if (!dcc_sanitycheck)
1099     return 1;
1100     if (prt < 1) {
1101     putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible port of %u!",
1102     nick, from, prt);
1103     return 0;
1104     }
1105 fabian 1.3 sprintf(badaddress, "%u.%u.%u.%u", (ip >> 24) & 0xff, (ip >> 16) & 0xff,
1106     (ip >> 8) & 0xff, ip & 0xff);
1107 segfault 1.1 if (ip < (1 << 24)) {
1108     putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible IP of %s!",
1109     nick, from, badaddress);
1110     return 0;
1111     }
1112 fabian 1.3 return 1;
1113     }
1114    
1115     int hostsanitycheck_dcc(char *nick, char *from, IP ip, char *dnsname,
1116     char *prt)
1117     {
1118     /* According to the latest RFC, the clients SHOULD be able to handle
1119 fabian 1.16 * DNS names that are up to 255 characters long. This is not broken.
1120     */
1121 fabian 1.23 char hostn[256], badaddress[16];
1122 fabian 1.3
1123     /* It is disabled HERE so we only have to check in *one* spot! */
1124     if (!dcc_sanitycheck)
1125     return 1;
1126     sprintf(badaddress, "%u.%u.%u.%u", (ip >> 24) & 0xff, (ip >> 16) & 0xff,
1127     (ip >> 8) & 0xff, ip & 0xff);
1128 segfault 1.1 /* These should pad like crazy with zeros, since 120 bytes or so is
1129     * where the routines providing our data currently lose interest. I'm
1130 fabian 1.16 * using the n-variant in case someone changes that...
1131     */
1132 guppy 1.27 strncpyz(hostn, extracthostname(from), sizeof hostn);
1133 fabian 1.23 if (!egg_strcasecmp(hostn, dnsname)) {
1134 segfault 1.1 putlog(LOG_DEBUG, "*", "DNS information for submitted IP checks out.");
1135     return 1;
1136     }
1137     if (!strcmp(badaddress, dnsname))
1138 fabian 1.23 putlog(LOG_MISC, "*", "ALERT: (%s!%s) sent a DCC request with bogus IP "
1139     "information of %s port %u!", nick, from, badaddress, prt);
1140 segfault 1.1 else
1141     return 1; /* <- usually happens when we have
1142 fabian 1.16 a user with an unresolved hostmask! */
1143 segfault 1.1 return 0;
1144 fabian 1.15 }
1145    
1146     /* Checks wether the referenced socket has data queued.
1147     *
1148     * Returns true if the incoming/outgoing (depending on 'type') queues
1149     * contain data, otherwise false.
1150     */
1151     int sock_has_data(int type, int sock)
1152     {
1153     int ret = 0, i;
1154    
1155     for (i = 0; i < MAXSOCKS; i++)
1156 fabian 1.22 if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock)
1157 fabian 1.15 break;
1158     if (i < MAXSOCKS) {
1159     switch (type) {
1160     case SOCK_DATA_OUTGOING:
1161     ret = (socklist[i].outbuf != NULL);
1162     break;
1163     case SOCK_DATA_INCOMING:
1164     ret = (socklist[i].inbuf != NULL);
1165     break;
1166     }
1167 fabian 1.22 } else
1168     debug1("sock_has_data: could not find socket #%d, returning false.", sock);
1169 fabian 1.15 return ret;
1170 poptix 1.30 }
1171    
1172     /* flush_inbuf():
1173     * checks if there's data in the incoming buffer of an connection
1174     * and flushs the buffer if possible
1175     *
1176     * returns: -1 if the dcc entry wasn't found
1177     * -2 if dcc[idx].type->activity doesn't exist and the data couldn't
1178     * be handled
1179     * 0 if buffer was empty
1180     * otherwise length of flushed buffer
1181     */
1182     int flush_inbuf(int idx)
1183     {
1184     int i, len;
1185     char *inbuf;
1186    
1187     Assert((idx >= 0) && (idx < dcc_total));
1188     for (i = 0; i < MAXSOCKS; i++) {
1189     if ((dcc[idx].sock == socklist[i].sock)
1190     && !(socklist[i].flags & SOCK_UNUSED)) {
1191     len = socklist[i].inbuflen;
1192     if ((len > 0) && socklist[i].inbuf) {
1193     if (dcc[idx].type && dcc[idx].type->activity) {
1194     inbuf = socklist[i].inbuf;
1195     socklist[i].inbuf = NULL;
1196     dcc[idx].type->activity(idx, inbuf, len);
1197     nfree(inbuf);
1198     return len;
1199     } else
1200     return -2;
1201     } else
1202     return 0;
1203     }
1204     }
1205     return -1;
1206 segfault 1.1 }

webmaster@eggheads.org
ViewVC Help
Powered by ViewVC 1.1.23