1 |
/* |
2 |
* net.c -- handles: |
3 |
* all raw network i/o |
4 |
* |
5 |
* This is hereby released into the public domain. |
6 |
* Robey Pointer, robey@netcom.com |
7 |
*/ |
8 |
|
9 |
#include "main.h" |
10 |
#include <limits.h> |
11 |
#include <string.h> |
12 |
#include <netdb.h> |
13 |
#include <sys/socket.h> |
14 |
#if HAVE_SYS_SELECT_H |
15 |
#include <sys/select.h> |
16 |
#endif |
17 |
#include <netinet/in.h> |
18 |
#include <arpa/inet.h> /* is this really necessary? */ |
19 |
#include <errno.h> |
20 |
#if HAVE_UNISTD_H |
21 |
#include <unistd.h> |
22 |
#endif |
23 |
#include <fcntl.h> |
24 |
#include <setjmp.h> |
25 |
|
26 |
#if !HAVE_GETDTABLESIZE |
27 |
#ifdef FD_SETSIZE |
28 |
#define getdtablesize() FD_SETSIZE |
29 |
#else |
30 |
#define getdtablesize() 200 |
31 |
#endif |
32 |
#endif |
33 |
|
34 |
extern int backgrd; |
35 |
extern int use_stderr; |
36 |
extern int resolve_timeout; |
37 |
|
38 |
char hostname[121] = ""; /* hostname can be specified in the config |
39 |
* file */ |
40 |
char myip[121] = ""; /* IP can be specified in the config file */ |
41 |
char firewall[121] = ""; /* socks server for firewall */ |
42 |
int firewallport = 1080; /* default port of Sock4/5 firewalls */ |
43 |
char botuser[21] = "eggdrop"; /* username of the user running the bot */ |
44 |
int dcc_sanitycheck = 0; /* we should do some sanity checking on dcc |
45 |
* connections. */ |
46 |
sock_list *socklist = 0; /* enough to be safe */ |
47 |
int MAXSOCKS = 0; |
48 |
|
49 |
/* types of proxy */ |
50 |
#define PROXY_SOCKS 1 |
51 |
#define PROXY_SUN 2 |
52 |
|
53 |
jmp_buf alarmret; /* env buffer for alarm() returns */ |
54 |
|
55 |
/* i need an UNSIGNED long for dcc type stuff */ |
56 |
IP my_atoul(char *s) |
57 |
{ |
58 |
IP ret = 0; |
59 |
|
60 |
while ((*s >= '0') && (*s <= '9')) { |
61 |
ret *= 10; |
62 |
ret += ((*s) - '0'); |
63 |
s++; |
64 |
} |
65 |
return ret; |
66 |
} |
67 |
|
68 |
#define my_ntohs(sh) swap_short(sh) |
69 |
#define my_htons(sh) swap_short(sh) |
70 |
#define my_ntohl(ln) swap_long(ln) |
71 |
#define my_htonl(ln) swap_long(ln) |
72 |
|
73 |
/* i read somewhere that memcpy() is broken on some machines */ |
74 |
/* it's easy to replace, so i'm not gonna take any chances, because |
75 |
* it's pretty important that it work correctly here */ |
76 |
void my_memcpy(char *dest, char *src, int len) |
77 |
{ |
78 |
while (len--) |
79 |
*dest++ = *src++; |
80 |
} |
81 |
|
82 |
#ifndef HAVE_BZERO |
83 |
/* bzero() is bsd-only, so here's one for non-bsd systems */ |
84 |
void bzero(char *dest, int len) |
85 |
{ |
86 |
while (len--) |
87 |
*dest++ = 0; |
88 |
} |
89 |
#endif |
90 |
|
91 |
/* initialize the socklist */ |
92 |
void init_net() |
93 |
{ |
94 |
int i; |
95 |
|
96 |
for (i = 0; i < MAXSOCKS; i++) { |
97 |
socklist[i].flags = SOCK_UNUSED; |
98 |
} |
99 |
} |
100 |
|
101 |
int expmem_net() |
102 |
{ |
103 |
int i, tot = 0; |
104 |
|
105 |
context; |
106 |
for (i = 0; i < MAXSOCKS; i++) { |
107 |
if (!(socklist[i].flags & SOCK_UNUSED)) { |
108 |
if (socklist[i].inbuf != NULL) |
109 |
tot += strlen(socklist[i].inbuf) + 1; |
110 |
if (socklist[i].outbuf != NULL) |
111 |
tot += socklist[i].outbuflen; |
112 |
} |
113 |
} |
114 |
return tot; |
115 |
} |
116 |
|
117 |
/* get my ip number */ |
118 |
IP getmyip() |
119 |
{ |
120 |
struct hostent *hp; |
121 |
char s[121]; |
122 |
IP ip; |
123 |
struct in_addr *in; |
124 |
|
125 |
/* could be pre-defined */ |
126 |
if (myip[0]) { |
127 |
if ((myip[strlen(myip) - 1] >= '0') && (myip[strlen(myip) - 1] <= '9')) |
128 |
return (IP) inet_addr(myip); |
129 |
} |
130 |
/* also could be pre-defined */ |
131 |
if (hostname[0]) |
132 |
hp = gethostbyname(hostname); |
133 |
else { |
134 |
gethostname(s, 120); |
135 |
hp = gethostbyname(s); |
136 |
} |
137 |
if (hp == NULL) |
138 |
fatal("Hostname self-lookup failed.", 0); |
139 |
in = (struct in_addr *) (hp->h_addr_list[0]); |
140 |
ip = (IP) (in->s_addr); |
141 |
return ip; |
142 |
} |
143 |
|
144 |
void neterror(char *s) |
145 |
{ |
146 |
switch (errno) { |
147 |
case EADDRINUSE: |
148 |
strcpy(s, "Address already in use"); |
149 |
break; |
150 |
case EADDRNOTAVAIL: |
151 |
strcpy(s, "Address invalid on remote machine"); |
152 |
break; |
153 |
case EAFNOSUPPORT: |
154 |
strcpy(s, "Address family not supported"); |
155 |
break; |
156 |
case EALREADY: |
157 |
strcpy(s, "Socket already in use"); |
158 |
break; |
159 |
case EBADF: |
160 |
strcpy(s, "Socket descriptor is bad"); |
161 |
break; |
162 |
case ECONNREFUSED: |
163 |
strcpy(s, "Connection refused"); |
164 |
break; |
165 |
case EFAULT: |
166 |
strcpy(s, "Namespace segment violation"); |
167 |
break; |
168 |
case EINPROGRESS: |
169 |
strcpy(s, "Operation in progress"); |
170 |
break; |
171 |
case EINTR: |
172 |
strcpy(s, "Timeout"); |
173 |
break; |
174 |
case EINVAL: |
175 |
strcpy(s, "Invalid namespace"); |
176 |
break; |
177 |
case EISCONN: |
178 |
strcpy(s, "Socket already connected"); |
179 |
break; |
180 |
case ENETUNREACH: |
181 |
strcpy(s, "Network unreachable"); |
182 |
break; |
183 |
case ENOTSOCK: |
184 |
strcpy(s, "File descriptor, not a socket"); |
185 |
break; |
186 |
case ETIMEDOUT: |
187 |
strcpy(s, "Connection timed out"); |
188 |
break; |
189 |
case ENOTCONN: |
190 |
strcpy(s, "Socket is not connected"); |
191 |
break; |
192 |
case EHOSTUNREACH: |
193 |
strcpy(s, "Host is unreachable"); |
194 |
break; |
195 |
case EPIPE: |
196 |
strcpy(s, "Broken pipe"); |
197 |
break; |
198 |
#ifdef ECONNRESET |
199 |
case ECONNRESET: |
200 |
strcpy(s, "Connection reset by peer"); |
201 |
break; |
202 |
#endif |
203 |
#ifdef EACCES |
204 |
case EACCES: |
205 |
strcpy(s, "Permission denied"); |
206 |
break; |
207 |
#endif |
208 |
case 0: |
209 |
strcpy(s, "Error 0"); |
210 |
break; |
211 |
default: |
212 |
sprintf(s, "Unforseen error %d", errno); |
213 |
break; |
214 |
} |
215 |
} |
216 |
|
217 |
/* request a normal socket for i/o */ |
218 |
void setsock(int sock, int options) |
219 |
{ |
220 |
int i; |
221 |
int parm; |
222 |
|
223 |
for (i = 0; i < MAXSOCKS; i++) { |
224 |
if (socklist[i].flags & SOCK_UNUSED) { |
225 |
/* yay! there is table space */ |
226 |
socklist[i].inbuf = socklist[i].outbuf = NULL; |
227 |
socklist[i].outbuflen = 0; |
228 |
socklist[i].flags = options; |
229 |
socklist[i].sock = sock; |
230 |
if (((sock != STDOUT) || backgrd) && |
231 |
!(socklist[i].flags & SOCK_NONSOCK)) { |
232 |
parm = 1; |
233 |
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &parm, |
234 |
sizeof(int)); |
235 |
|
236 |
parm = 0; |
237 |
setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &parm, sizeof(int)); |
238 |
} |
239 |
if (options & SOCK_LISTEN) { |
240 |
/* Tris says this lets us grab the same port again next time */ |
241 |
parm = 1; |
242 |
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &parm, |
243 |
sizeof(int)); |
244 |
} |
245 |
/* yay async i/o ! */ |
246 |
fcntl(sock, F_SETFL, O_NONBLOCK); |
247 |
return; |
248 |
} |
249 |
} |
250 |
fatal("Socket table is full!", 0); |
251 |
} |
252 |
|
253 |
int getsock(int options) |
254 |
{ |
255 |
int sock = socket(AF_INET, SOCK_STREAM, 0); |
256 |
|
257 |
if (sock < 0) |
258 |
fatal("Can't open a socket at all!", 0); |
259 |
setsock(sock, options); |
260 |
return sock; |
261 |
} |
262 |
|
263 |
/* done with a socket */ |
264 |
void killsock(int sock) |
265 |
{ |
266 |
int i; |
267 |
|
268 |
for (i = 0; i < MAXSOCKS; i++) { |
269 |
if (socklist[i].sock == sock) { |
270 |
close(socklist[i].sock); |
271 |
if (socklist[i].inbuf != NULL) { |
272 |
nfree(socklist[i].inbuf); |
273 |
socklist[i].inbuf = NULL; |
274 |
} |
275 |
if (socklist[i].outbuf != NULL) { |
276 |
nfree(socklist[i].outbuf); |
277 |
socklist[i].outbuf = NULL; |
278 |
socklist[i].outbuflen = 0; |
279 |
} |
280 |
socklist[i].flags = SOCK_UNUSED; |
281 |
return; |
282 |
} |
283 |
} |
284 |
putlog(LOG_MISC, "*", "Attempt to kill un-allocated socket %d !!", sock); |
285 |
} |
286 |
|
287 |
/* send connection request to proxy */ |
288 |
static int proxy_connect(int sock, char *host, int port, int proxy) |
289 |
{ |
290 |
unsigned char x[10]; |
291 |
struct hostent *hp; |
292 |
char s[30]; |
293 |
|
294 |
/* socks proxy */ |
295 |
if (proxy == PROXY_SOCKS) { |
296 |
/* numeric IP? */ |
297 |
if ((host[strlen(host) - 1] >= '0') && (host[strlen(host) - 1] <= '9')) { |
298 |
IP ip = ((IP) inet_addr(host)); /* drummer */ |
299 |
my_memcpy((char *) x, (char *) &ip, 4); /* Beige@Efnet */ |
300 |
} else { |
301 |
/* no, must be host.domain */ |
302 |
if (!setjmp(alarmret)) { |
303 |
alarm(resolve_timeout); |
304 |
hp = gethostbyname(host); |
305 |
alarm(0); |
306 |
} else { |
307 |
hp = NULL; |
308 |
} |
309 |
if (hp == NULL) { |
310 |
killsock(sock); |
311 |
return -2; |
312 |
} |
313 |
my_memcpy((char *) x, (char *) hp->h_addr, hp->h_length); |
314 |
} |
315 |
sprintf(s, "\004\001%c%c%c%c%c%c%s", (port >> 8) % 256, (port % 256), |
316 |
x[0], x[1], x[2], x[3], botuser); |
317 |
write(sock, s, strlen(botuser) + 9); |
318 |
} else if (proxy == PROXY_SUN) { |
319 |
sprintf(s, "%s %d\n", host, port); |
320 |
write(sock, s, strlen(s)); |
321 |
} |
322 |
return sock; |
323 |
} |
324 |
|
325 |
/* starts a connection attempt to a socket |
326 |
* returns <0 if connection refused: |
327 |
* -1 neterror() type error |
328 |
* -2 can't resolve hostname */ |
329 |
int open_telnet_raw(int sock, char *server, int sport) |
330 |
{ |
331 |
struct sockaddr_in name; |
332 |
struct hostent *hp; |
333 |
char host[121]; |
334 |
int i, port; |
335 |
volatile int proxy; |
336 |
|
337 |
/* firewall? use socks */ |
338 |
if (firewall[0]) { |
339 |
if (firewall[0] == '!') { |
340 |
proxy = PROXY_SUN; |
341 |
strcpy(host, &firewall[1]); |
342 |
} else { |
343 |
proxy = PROXY_SOCKS; |
344 |
strcpy(host, firewall); |
345 |
} |
346 |
port = firewallport; |
347 |
} else { |
348 |
proxy = 0; |
349 |
strcpy(host, server); |
350 |
port = sport; |
351 |
} |
352 |
/* patch by tris for multi-hosted machines: */ |
353 |
bzero((char *) &name, sizeof(struct sockaddr_in)); |
354 |
|
355 |
name.sin_family = AF_INET; |
356 |
name.sin_addr.s_addr = (myip[0] ? getmyip() : INADDR_ANY); |
357 |
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) { |
358 |
killsock(sock); |
359 |
return -1; |
360 |
} |
361 |
bzero((char *) &name, sizeof(struct sockaddr_in)); |
362 |
|
363 |
name.sin_family = AF_INET; |
364 |
name.sin_port = my_htons(port); |
365 |
/* numeric IP? */ |
366 |
if ((host[strlen(host) - 1] >= '0') && (host[strlen(host) - 1] <= '9')) |
367 |
name.sin_addr.s_addr = inet_addr(host); |
368 |
else { |
369 |
/* no, must be host.domain */ |
370 |
if (!setjmp(alarmret)) { |
371 |
alarm(resolve_timeout); |
372 |
hp = gethostbyname(host); |
373 |
alarm(0); |
374 |
} else { |
375 |
hp = NULL; |
376 |
} |
377 |
if (hp == NULL) { |
378 |
killsock(sock); |
379 |
return -2; |
380 |
} |
381 |
my_memcpy((char *) &name.sin_addr, hp->h_addr, hp->h_length); |
382 |
name.sin_family = hp->h_addrtype; |
383 |
} |
384 |
for (i = 0; i < MAXSOCKS; i++) { |
385 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == sock)) |
386 |
socklist[i].flags |= SOCK_CONNECT; |
387 |
} |
388 |
if (connect(sock, (struct sockaddr *) &name, |
389 |
sizeof(struct sockaddr_in)) < 0) { |
390 |
if (errno == EINPROGRESS) { |
391 |
/* firewall? announce connect attempt to proxy */ |
392 |
if (firewall[0]) |
393 |
return proxy_connect(sock, server, sport, proxy); |
394 |
return sock; /* async success! */ |
395 |
} else { |
396 |
killsock(sock); |
397 |
return -1; |
398 |
} |
399 |
} |
400 |
/* synchronous? :/ */ |
401 |
if (firewall[0]) |
402 |
return proxy_connect(sock, server, sport, proxy); |
403 |
return sock; |
404 |
} |
405 |
|
406 |
/* ordinary non-binary connection attempt */ |
407 |
int open_telnet(char *server, int port) |
408 |
{ |
409 |
return open_telnet_raw(getsock(0), server, port); |
410 |
} |
411 |
|
412 |
/* returns a socket number for a listening socket that will accept any |
413 |
* connection -- port # is returned in port */ |
414 |
int open_listen(int *port) |
415 |
{ |
416 |
int sock, addrlen; |
417 |
struct sockaddr_in name; |
418 |
|
419 |
if (firewall[0]) { |
420 |
/* FIXME: can't do listen port thru firewall yet */ |
421 |
putlog(LOG_ALL, "*", "!! Cant open a listen port (you are using a firewall)\n"); |
422 |
return -1; |
423 |
} |
424 |
sock = getsock(SOCK_LISTEN); |
425 |
bzero((char *) &name, sizeof(struct sockaddr_in)); |
426 |
|
427 |
name.sin_family = AF_INET; |
428 |
name.sin_port = my_htons(*port); /* 0 = just assign us a port */ |
429 |
name.sin_addr.s_addr = (myip[0] ? getmyip() : INADDR_ANY); |
430 |
if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) { |
431 |
killsock(sock); |
432 |
return -1; |
433 |
} |
434 |
/* what port are we on? */ |
435 |
addrlen = sizeof(name); |
436 |
if (getsockname(sock, (struct sockaddr *) &name, &addrlen) < 0) { |
437 |
killsock(sock); |
438 |
return -1; |
439 |
} |
440 |
*port = my_ntohs(name.sin_port); |
441 |
if (listen(sock, 1) < 0) { |
442 |
killsock(sock); |
443 |
return -1; |
444 |
} |
445 |
return sock; |
446 |
} |
447 |
|
448 |
/* given network-style IP address, return hostname */ |
449 |
/* hostname will be "##.##.##.##" format if there was an error */ |
450 |
char *hostnamefromip(unsigned long ip) |
451 |
{ |
452 |
struct hostent *hp; |
453 |
unsigned long addr = ip; |
454 |
unsigned char *p; |
455 |
static char s[121]; |
456 |
|
457 |
if (!setjmp(alarmret)) { |
458 |
alarm(resolve_timeout); |
459 |
hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); |
460 |
alarm(0); |
461 |
} else { |
462 |
hp = NULL; |
463 |
} |
464 |
if (hp == NULL) { |
465 |
p = (unsigned char *) &addr; |
466 |
sprintf(s, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); |
467 |
return s; |
468 |
} |
469 |
strncpy(s, hp->h_name, 120); |
470 |
s[120] = 0; |
471 |
return s; |
472 |
} |
473 |
|
474 |
/* short routine to answer a connect received on a socket made previously |
475 |
* by open_listen ... returns hostname of the caller & the new socket |
476 |
* does NOT dispose of old "public" socket! */ |
477 |
int answer(int sock, char *caller, unsigned long *ip, |
478 |
unsigned short *port, int binary) |
479 |
{ |
480 |
int new_sock, addrlen; |
481 |
struct sockaddr_in from; |
482 |
addrlen = sizeof(struct sockaddr); |
483 |
|
484 |
new_sock = accept(sock, (struct sockaddr *) &from, &addrlen); |
485 |
if (new_sock < 0) |
486 |
return -1; |
487 |
if (ip != NULL) { |
488 |
*ip = from.sin_addr.s_addr; |
489 |
strncpy(caller, hostnamefromip(*ip), 120); |
490 |
caller[120] = 0; |
491 |
*ip = my_ntohl(*ip); |
492 |
} |
493 |
if (port != NULL) |
494 |
*port = my_ntohs(from.sin_port); |
495 |
/* set up all the normal socket crap */ |
496 |
setsock(new_sock, (binary ? SOCK_BINARY : 0)); |
497 |
return new_sock; |
498 |
} |
499 |
|
500 |
/* like open_telnet, but uses server & port specifications of dcc */ |
501 |
int open_telnet_dcc(int sock, char *server, char *port) |
502 |
{ |
503 |
int p; |
504 |
unsigned long addr; |
505 |
char sv[121]; |
506 |
unsigned char c[4]; |
507 |
|
508 |
if (port != NULL) |
509 |
p = atoi(port); |
510 |
else |
511 |
p = 2000; |
512 |
if (server != NULL) |
513 |
addr = my_atoul(server); |
514 |
else |
515 |
addr = 0L; |
516 |
if (addr < (1 << 24)) |
517 |
return -3; /* fake address */ |
518 |
c[0] = (addr >> 24) & 0xff; |
519 |
c[1] = (addr >> 16) & 0xff; |
520 |
c[2] = (addr >> 8) & 0xff; |
521 |
c[3] = addr & 0xff; |
522 |
sprintf(sv, "%u.%u.%u.%u", c[0], c[1], c[2], c[3]); |
523 |
/* strcpy(sv,hostnamefromip(addr)); */ |
524 |
p = open_telnet_raw(sock, sv, p); |
525 |
return p; |
526 |
} |
527 |
|
528 |
/* all new replacements for mtgets/mtread */ |
529 |
|
530 |
/* attempts to read from all the sockets in socklist |
531 |
* fills s with up to 511 bytes if available, and returns the array index |
532 |
* on EOF, returns -1, with socket in len |
533 |
* on socket error, returns -2 |
534 |
* if nothing is ready, returns -3 */ |
535 |
static int sockread(char *s, int *len) |
536 |
{ |
537 |
fd_set fd; |
538 |
int fds, i, x; |
539 |
struct timeval t; |
540 |
int grab = 511; |
541 |
|
542 |
fds = getdtablesize(); |
543 |
#ifdef FD_SETSIZE |
544 |
if (fds > FD_SETSIZE) |
545 |
fds = FD_SETSIZE; /* fixes YET ANOTHER freebsd bug!!! */ |
546 |
#endif |
547 |
/* timeout: 1 sec */ |
548 |
t.tv_sec = 1; |
549 |
t.tv_usec = 0; |
550 |
FD_ZERO(&fd); |
551 |
for (i = 0; i < MAXSOCKS; i++) |
552 |
if (!(socklist[i].flags & SOCK_UNUSED)) { |
553 |
if ((socklist[i].sock == STDOUT) && !backgrd) |
554 |
FD_SET(STDIN, &fd); |
555 |
else |
556 |
FD_SET(socklist[i].sock, &fd); |
557 |
} |
558 |
#ifdef HPUX_HACKS |
559 |
#ifndef HPUX10_HACKS |
560 |
x = select(fds, (int *) &fd, (int *) NULL, (int *) NULL, &t); |
561 |
#else |
562 |
x = select(fds, &fd, NULL, NULL, &t); |
563 |
#endif |
564 |
#else |
565 |
x = select(fds, &fd, NULL, NULL, &t); |
566 |
#endif |
567 |
if (x > 0) { |
568 |
/* something happened */ |
569 |
for (i = 0; i < MAXSOCKS; i++) { |
570 |
if ((!(socklist[i].flags & SOCK_UNUSED)) && |
571 |
((FD_ISSET(socklist[i].sock, &fd)) || |
572 |
((socklist[i].sock == STDOUT) && (!backgrd) && |
573 |
(FD_ISSET(STDIN, &fd))))) { |
574 |
if (socklist[i].flags & (SOCK_LISTEN | SOCK_CONNECT)) { |
575 |
/* listening socket -- don't read, just return activity */ |
576 |
/* same for connection attempt */ |
577 |
/* (for strong connections, require a read to succeed first) */ |
578 |
if ((firewall[0]) && (firewall[0] != '!') && |
579 |
(socklist[i].flags & SOCK_CONNECT)) { |
580 |
/* hang around to get the return code from proxy */ |
581 |
grab = 8; |
582 |
} else if (!(socklist[i].flags & SOCK_STRONGCONN)) { |
583 |
debug1("net: connect! sock %d", socklist[i].sock); |
584 |
s[0] = 0; |
585 |
*len = 0; |
586 |
return i; |
587 |
} |
588 |
} |
589 |
if ((socklist[i].sock == STDOUT) && !backgrd) |
590 |
x = read(STDIN, s, grab); |
591 |
else |
592 |
x = read(socklist[i].sock, s, grab); |
593 |
if (x <= 0) { /* eof */ |
594 |
if (x == EAGAIN) { |
595 |
s[0] = 0; |
596 |
*len = 0; |
597 |
return -3; |
598 |
} |
599 |
*len = socklist[i].sock; |
600 |
socklist[i].flags &= ~SOCK_CONNECT; |
601 |
debug1("net: eof!(read) socket %d", socklist[i].sock); |
602 |
return -1; |
603 |
} |
604 |
s[x] = 0; |
605 |
*len = x; |
606 |
if ((firewall[0]) && (socklist[i].flags & SOCK_CONNECT)) { |
607 |
switch (s[1]) { |
608 |
case 90: /* success */ |
609 |
s[0] = 0; |
610 |
*len = 0; |
611 |
return i; |
612 |
case 91: /* failed */ |
613 |
errno = ECONNREFUSED; |
614 |
break; |
615 |
case 92: /* no identd */ |
616 |
case 93: /* identd said wrong username */ |
617 |
errno = ENETUNREACH; |
618 |
break; |
619 |
/* a better error message would be "socks misconfigured" */ |
620 |
/* or "identd not working" but this is simplest */ |
621 |
} |
622 |
*len = socklist[i].sock; |
623 |
socklist[i].flags &= ~SOCK_CONNECT; |
624 |
return -1; |
625 |
} |
626 |
return i; |
627 |
} |
628 |
} |
629 |
} else if (x == -1) |
630 |
return -2; /* socket error */ |
631 |
else { |
632 |
s[0] = 0; |
633 |
*len = 0; |
634 |
} |
635 |
return -3; |
636 |
} |
637 |
|
638 |
/* sockgets: buffer and read from sockets |
639 |
* |
640 |
* attempts to read from all registered sockets for up to one second. if |
641 |
* after one second, no complete data has been received from any of the |
642 |
* sockets, 's' will be empty, 'len' will be 0, and sockgets will return -3. |
643 |
* if there is returnable data received from a socket, the data will be |
644 |
* in 's' (null-terminated if non-binary), the length will be returned |
645 |
* in len, and the socket number will be returned. |
646 |
* normal sockets have their input buffered, and each call to sockgets |
647 |
* will return one line terminated with a '\n'. binary sockets are not |
648 |
* buffered and return whatever coems in as soon as it arrives. |
649 |
* listening sockets will return an empty string when a connection comes in. |
650 |
* connecting sockets will return an empty string on a successful connect, |
651 |
* or EOF on a failed connect. |
652 |
* if an EOF is detected from any of the sockets, that socket number will be |
653 |
* put in len, and -1 will be returned. |
654 |
* the maximum length of the string returned is 512 (including null) |
655 |
*/ |
656 |
|
657 |
int sockgets(char *s, int *len) |
658 |
{ |
659 |
char xx[514], *p, *px; |
660 |
int ret, i, data = 0; |
661 |
|
662 |
context; |
663 |
for (i = 0; i < MAXSOCKS; i++) { |
664 |
/* check for stored-up data waiting to be processed */ |
665 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].inbuf != NULL)) { |
666 |
/* look for \r too cos windows can't follow RFCs */ |
667 |
p = strchr(socklist[i].inbuf, '\n'); |
668 |
if (p == NULL) |
669 |
p = strchr(socklist[i].inbuf, '\r'); |
670 |
if (p != NULL) { |
671 |
*p = 0; |
672 |
if (strlen(socklist[i].inbuf) > 510) |
673 |
socklist[i].inbuf[510] = 0; |
674 |
strcpy(s, socklist[i].inbuf); |
675 |
px = (char *) nmalloc(strlen(p + 1) + 1); |
676 |
strcpy(px, p + 1); |
677 |
nfree(socklist[i].inbuf); |
678 |
if (px[0]) |
679 |
socklist[i].inbuf = px; |
680 |
else { |
681 |
nfree(px); |
682 |
socklist[i].inbuf = NULL; |
683 |
} |
684 |
/* strip CR if this was CR/LF combo */ |
685 |
if (s[strlen(s) - 1] == '\r') |
686 |
s[strlen(s) - 1] = 0; |
687 |
*len = strlen(s); |
688 |
return socklist[i].sock; |
689 |
} |
690 |
} |
691 |
/* also check any sockets that might have EOF'd during write */ |
692 |
if (!(socklist[i].flags & SOCK_UNUSED) |
693 |
&& (socklist[i].flags & SOCK_EOFD)) { |
694 |
context; |
695 |
s[0] = 0; |
696 |
*len = socklist[i].sock; |
697 |
return -1; |
698 |
} |
699 |
} |
700 |
/* no pent-up data of any worth -- down to business */ |
701 |
context; |
702 |
*len = 0; |
703 |
ret = sockread(xx, len); |
704 |
if (ret < 0) { |
705 |
s[0] = 0; |
706 |
return ret; |
707 |
} |
708 |
/* binary and listening sockets don't get buffered */ |
709 |
if (socklist[ret].flags & SOCK_CONNECT) { |
710 |
if (socklist[ret].flags & SOCK_STRONGCONN) { |
711 |
socklist[ret].flags &= ~SOCK_STRONGCONN; |
712 |
/* buffer any data that came in, for future read */ |
713 |
socklist[ret].inbuf = (char *) nmalloc(strlen(xx) + 1); |
714 |
strcpy(socklist[ret].inbuf, xx); |
715 |
} |
716 |
socklist[ret].flags &= ~SOCK_CONNECT; |
717 |
s[0] = 0; |
718 |
return socklist[ret].sock; |
719 |
} |
720 |
if (socklist[ret].flags & SOCK_BINARY) { |
721 |
my_memcpy(s, xx, *len); |
722 |
return socklist[ret].sock; |
723 |
} |
724 |
if (socklist[ret].flags & SOCK_LISTEN) |
725 |
return socklist[ret].sock; |
726 |
context; |
727 |
/* might be necessary to prepend stored-up data! */ |
728 |
if (socklist[ret].inbuf != NULL) { |
729 |
p = socklist[ret].inbuf; |
730 |
socklist[ret].inbuf = (char *) nmalloc(strlen(p) + strlen(xx) + 1); |
731 |
strcpy(socklist[ret].inbuf, p); |
732 |
strcat(socklist[ret].inbuf, xx); |
733 |
nfree(p); |
734 |
if (strlen(socklist[ret].inbuf) < 512) { |
735 |
strcpy(xx, socklist[ret].inbuf); |
736 |
nfree(socklist[ret].inbuf); |
737 |
socklist[ret].inbuf = NULL; |
738 |
} else { |
739 |
p = socklist[ret].inbuf; |
740 |
socklist[ret].inbuf = (char *) nmalloc(strlen(p) - 509); |
741 |
strcpy(socklist[ret].inbuf, p + 510); |
742 |
*(p + 510) = 0; |
743 |
strcpy(xx, p); |
744 |
nfree(p); |
745 |
/* (leave the rest to be post-pended later) */ |
746 |
} |
747 |
} |
748 |
context; |
749 |
/* look for EOL marker; if it's there, i have something to show */ |
750 |
p = strchr(xx, '\n'); |
751 |
if (p == NULL) |
752 |
p = strchr(xx, '\r'); |
753 |
if (p != NULL) { |
754 |
*p = 0; |
755 |
strcpy(s, xx); |
756 |
strcpy(xx, p + 1); |
757 |
if (s[strlen(s) - 1] == '\r') |
758 |
s[strlen(s) - 1] = 0; |
759 |
data = 1; /* DCC_CHAT may now need to process a |
760 |
* blank line */ |
761 |
/* NO! */ |
762 |
/* if (!s[0]) strcpy(s," "); */ |
763 |
} else { |
764 |
s[0] = 0; |
765 |
if (strlen(xx) >= 510) { |
766 |
/* string is too long, so just insert fake \n */ |
767 |
strcpy(s, xx); |
768 |
xx[0] = 0; |
769 |
data = 1; |
770 |
} |
771 |
} |
772 |
context; |
773 |
*len = strlen(s); |
774 |
/* anything left that needs to be saved? */ |
775 |
if (!xx[0]) { |
776 |
if (data) |
777 |
return socklist[ret].sock; |
778 |
else |
779 |
return -3; |
780 |
} |
781 |
context; |
782 |
/* prepend old data back */ |
783 |
if (socklist[ret].inbuf != NULL) { |
784 |
contextnote("dw's bug"); |
785 |
p = socklist[ret].inbuf; |
786 |
socklist[ret].inbuf = (char *) nmalloc(strlen(p) + strlen(xx) + 1); |
787 |
strcpy(socklist[ret].inbuf, xx); |
788 |
strcat(socklist[ret].inbuf, p); |
789 |
nfree(p); |
790 |
} else { |
791 |
contextnote("dw's bug"); |
792 |
socklist[ret].inbuf = (char *) nmalloc(strlen(xx) + 1); |
793 |
strcpy(socklist[ret].inbuf, xx); |
794 |
} |
795 |
contextnote("dw's bug"); |
796 |
if (data) { |
797 |
contextnote("dw's bug"); |
798 |
return socklist[ret].sock; |
799 |
} else { |
800 |
contextnote("dw's bug"); |
801 |
return -3; |
802 |
} |
803 |
} |
804 |
|
805 |
/* dump something to a socket */ |
806 |
/* DO NOT PUT CONTEXTS IN HERE IF YOU WANT DEBUG TO BE MEANINGFUL!!! */ |
807 |
void tputs(int z, char *s, unsigned int len) |
808 |
{ |
809 |
int i, x; |
810 |
char *p; |
811 |
|
812 |
if (z < 0) |
813 |
return; /* um... HELLO?! sanity check please! */ |
814 |
if (((z == STDOUT) || (z == STDERR)) && (!backgrd || use_stderr)) { |
815 |
write(z, s, len); |
816 |
return; |
817 |
} |
818 |
for (i = 0; i < MAXSOCKS; i++) { |
819 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == z)) { |
820 |
if (socklist[i].outbuf != NULL) { |
821 |
/* already queueing: just add it */ |
822 |
p = (char *) nrealloc(socklist[i].outbuf, socklist[i].outbuflen + len); |
823 |
my_memcpy(p + socklist[i].outbuflen, s, len); |
824 |
socklist[i].outbuf = p; |
825 |
socklist[i].outbuflen += len; |
826 |
return; |
827 |
} |
828 |
/* try. */ |
829 |
x = write(z, s, len); |
830 |
if (x == (-1)) |
831 |
x = 0; |
832 |
if (x < len) { |
833 |
/* socket is full, queue it */ |
834 |
socklist[i].outbuf = (char *) nmalloc(len - x); |
835 |
my_memcpy(socklist[i].outbuf, &s[x], len - x); |
836 |
socklist[i].outbuflen = len - x; |
837 |
} |
838 |
return; |
839 |
} |
840 |
} |
841 |
putlog(LOG_MISC, "*", "!!! writing to nonexistent socket: %d", z); |
842 |
s[strlen(s) - 1] = 0; |
843 |
putlog(LOG_MISC, "*", "!-> '%s'", s); |
844 |
} |
845 |
|
846 |
/* tputs might queue data for sockets, let's dump as much of it as |
847 |
* possible */ |
848 |
void dequeue_sockets() |
849 |
{ |
850 |
int i, x; |
851 |
|
852 |
for (i = 0; i < MAXSOCKS; i++) { |
853 |
if (!(socklist[i].flags & SOCK_UNUSED) && |
854 |
(socklist[i].outbuf != NULL)) { |
855 |
/* trick tputs into doing the work */ |
856 |
x = write(socklist[i].sock, socklist[i].outbuf, |
857 |
socklist[i].outbuflen); |
858 |
if ((x < 0) && (errno != EAGAIN) |
859 |
#ifdef EBADSLT |
860 |
&& (errno != EBADSLT) |
861 |
#endif |
862 |
#ifdef ENOTCONN |
863 |
&& (errno != ENOTCONN) |
864 |
#endif |
865 |
) { |
866 |
/* this detects an EOF during writing */ |
867 |
debug3("net: eof!(write) socket %d (%s,%d)", socklist[i].sock, |
868 |
strerror(errno), errno); |
869 |
socklist[i].flags |= SOCK_EOFD; |
870 |
} else if (x == socklist[i].outbuflen) { |
871 |
/* if the whole buffer was sent, nuke it */ |
872 |
nfree(socklist[i].outbuf); |
873 |
socklist[i].outbuf = NULL; |
874 |
socklist[i].outbuflen = 0; |
875 |
} else if (x > 0) { |
876 |
char *p = socklist[i].outbuf; |
877 |
|
878 |
/* this removes any sent bytes from the beginning of the buffer */ |
879 |
socklist[i].outbuf = (char *) nmalloc(socklist[i].outbuflen - x); |
880 |
my_memcpy(socklist[i].outbuf, p + x, socklist[i].outbuflen - x); |
881 |
socklist[i].outbuflen -= x; |
882 |
nfree(p); |
883 |
} |
884 |
} |
885 |
} |
886 |
} |
887 |
|
888 |
/* DEBUGGING STUFF */ |
889 |
|
890 |
void tell_netdebug(int idx) |
891 |
{ |
892 |
int i; |
893 |
char s[80]; |
894 |
|
895 |
dprintf(idx, "Open sockets:"); |
896 |
for (i = 0; i < MAXSOCKS; i++) { |
897 |
if (!(socklist[i].flags & SOCK_UNUSED)) { |
898 |
sprintf(s, " %d", socklist[i].sock); |
899 |
if (socklist[i].flags & SOCK_BINARY) |
900 |
strcat(s, " (binary)"); |
901 |
if (socklist[i].flags & SOCK_LISTEN) |
902 |
strcat(s, " (listen)"); |
903 |
if (socklist[i].flags & SOCK_CONNECT) |
904 |
strcat(s, " (connecting)"); |
905 |
if (socklist[i].flags & SOCK_STRONGCONN) |
906 |
strcat(s, " (strong)"); |
907 |
if (socklist[i].flags & SOCK_NONSOCK) |
908 |
strcat(s, " (file)"); |
909 |
if (socklist[i].inbuf != NULL) |
910 |
sprintf(&s[strlen(s)], " (inbuf: %04X)", strlen(socklist[i].inbuf)); |
911 |
if (socklist[i].outbuf != NULL) |
912 |
sprintf(&s[strlen(s)], " (outbuf: %06lX)", socklist[i].outbuflen); |
913 |
strcat(s, ","); |
914 |
dprintf(idx, "%s", s); |
915 |
} |
916 |
} |
917 |
dprintf(idx, " done.\n"); |
918 |
} |
919 |
|
920 |
/* Security-flavoured sanity checking on DCC connections of all sorts can be |
921 |
* done with this routine. Feed it the proper information from your DCC |
922 |
* before you attempt the connection, and this will make an attempt at |
923 |
* figuring out if the connection is really that person, or someone screwing |
924 |
* around. It's not foolproof, but anything that fails this check probably |
925 |
* isn't going to work anyway due to masquerading firewalls, NAT routers, |
926 |
* or bugs in mIRC. */ |
927 |
int sanitycheck_dcc(char *nick, char *from, char *ipaddy, char *port) |
928 |
{ |
929 |
/* According to the latest RFC, the clients SHOULD be able to handle |
930 |
* DNS names that are up to 255 characters long. This is not broken. */ |
931 |
char hostname[256], dnsname[256], badaddress[16]; |
932 |
IP ip = my_atoul(ipaddy); |
933 |
int prt = atoi(port); |
934 |
|
935 |
/* It is disabled HERE so we only have to check in *one* spot! */ |
936 |
if (!dcc_sanitycheck) |
937 |
return 1; |
938 |
context; /* This should be pretty solid, but |
939 |
* something _might_ break. */ |
940 |
sprintf(badaddress, "%u.%u.%u.%u", (ip >> 24) & 0xff, (ip >> 16) & 0xff, |
941 |
(ip >> 8) & 0xff, ip & 0xff); |
942 |
if (prt < 1) { |
943 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible port of %u!", |
944 |
nick, from, prt); |
945 |
return 0; |
946 |
} |
947 |
if (ip < (1 << 24)) { |
948 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible IP of %s!", |
949 |
nick, from, badaddress); |
950 |
return 0; |
951 |
} |
952 |
/* These should pad like crazy with zeros, since 120 bytes or so is |
953 |
* where the routines providing our data currently lose interest. I'm |
954 |
* using the n-variant in case someone changes that... */ |
955 |
strncpy(hostname, extracthostname(from), 256); |
956 |
strncpy(dnsname, hostnamefromip(my_htonl(ip)), 256); |
957 |
if (!strcasecmp(hostname, dnsname)) { |
958 |
putlog(LOG_DEBUG, "*", "DNS information for submitted IP checks out."); |
959 |
return 1; |
960 |
} |
961 |
if (!strcmp(badaddress, dnsname)) |
962 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) sent a DCC request with bogus IP information of %s port %u!", |
963 |
nick, from, badaddress, prt); |
964 |
else |
965 |
return 1; /* <- usually happens when we have |
966 |
a user with an unresolved hostmask! */ |
967 |
return 0; |
968 |
} |