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