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