1 |
/* |
2 |
* net.c -- handles: |
3 |
* all raw network i/o |
4 |
* |
5 |
* $Id: net.c,v 1.4 2010/09/14 19:45:29 pseudo Exp $ |
6 |
*/ |
7 |
/* |
8 |
* This is hereby released into the public domain. |
9 |
* Robey Pointer, robey@netcom.com |
10 |
* |
11 |
* Changes after Feb 23, 1999 Copyright Eggheads Development Team |
12 |
* |
13 |
* Copyright (C) 1999 - 2010 Eggheads Development Team |
14 |
* |
15 |
* This program is free software; you can redistribute it and/or |
16 |
* modify it under the terms of the GNU General Public License |
17 |
* as published by the Free Software Foundation; either version 2 |
18 |
* of the License, or (at your option) any later version. |
19 |
* |
20 |
* This program is distributed in the hope that it will be useful, |
21 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 |
* GNU General Public License for more details. |
24 |
* |
25 |
* You should have received a copy of the GNU General Public License |
26 |
* along with this program; if not, write to the Free Software |
27 |
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
28 |
*/ |
29 |
|
30 |
#include <fcntl.h> |
31 |
#include "main.h" |
32 |
#include <limits.h> |
33 |
#include <string.h> |
34 |
#include <netdb.h> |
35 |
#include <sys/socket.h> |
36 |
#if HAVE_SYS_SELECT_H |
37 |
# include <sys/select.h> |
38 |
#endif |
39 |
#include <netinet/in.h> |
40 |
#include <arpa/inet.h> |
41 |
#include <errno.h> |
42 |
#if HAVE_UNISTD_H |
43 |
# include <unistd.h> |
44 |
#endif |
45 |
#include <setjmp.h> |
46 |
|
47 |
#ifdef TLS |
48 |
# include <openssl/err.h> |
49 |
#endif |
50 |
|
51 |
#ifndef HAVE_GETDTABLESIZE |
52 |
# ifdef FD_SETSIZE |
53 |
# define getdtablesize() FD_SETSIZE |
54 |
# else |
55 |
# define getdtablesize() 200 |
56 |
# endif |
57 |
#endif |
58 |
|
59 |
extern struct dcc_t *dcc; |
60 |
extern int backgrd, use_stderr, resolve_timeout, dcc_total; |
61 |
extern unsigned long otraffic_irc_today, otraffic_bn_today, otraffic_dcc_today, |
62 |
otraffic_filesys_today, otraffic_trans_today, |
63 |
otraffic_unknown_today; |
64 |
|
65 |
char natip[121] = ""; /* Public IPv4 to report for systems behind NAT */ |
66 |
char listen_ip[121] = ""; /* IP (or hostname) for listening sockets */ |
67 |
char vhost[121] = ""; /* IPv4 vhost for outgoing connections */ |
68 |
#ifdef IPV6 |
69 |
char vhost6[121] = ""; /* IPv6 vhost for outgoing connections */ |
70 |
int pref_af = 0; /* Prefer IPv6 over IPv4? */ |
71 |
#endif |
72 |
char firewall[121] = ""; /* Socks server for firewall. */ |
73 |
int firewallport = 1080; /* Default port of socks 4/5 firewalls. */ |
74 |
char botuser[21] = "eggdrop"; /* Username of the user running the bot. */ |
75 |
int dcc_sanitycheck = 0; /* Do some sanity checking on dcc connections. */ |
76 |
|
77 |
sock_list *socklist = NULL; /* Enough to be safe. */ |
78 |
sigjmp_buf alarmret; /* Env buffer for alarm() returns. */ |
79 |
|
80 |
/* Types of proxies */ |
81 |
#define PROXY_SOCKS 1 |
82 |
#define PROXY_SUN 2 |
83 |
|
84 |
|
85 |
/* I need an UNSIGNED long for dcc type stuff |
86 |
*/ |
87 |
IP my_atoul(char *s) |
88 |
{ |
89 |
IP ret = 0; |
90 |
|
91 |
while ((*s >= '0') && (*s <= '9')) { |
92 |
ret *= 10; |
93 |
ret += ((*s) - '0'); |
94 |
s++; |
95 |
} |
96 |
return ret; |
97 |
} |
98 |
|
99 |
int expmem_net() |
100 |
{ |
101 |
int i, tot = 0; |
102 |
struct threaddata *td = threaddata(); |
103 |
|
104 |
for (i = 0; i < td->MAXSOCKS; i++) { |
105 |
if (!(td->socklist[i].flags & (SOCK_UNUSED | SOCK_TCL))) { |
106 |
if (td->socklist[i].handler.sock.inbuf != NULL) |
107 |
tot += strlen(td->socklist[i].handler.sock.inbuf) + 1; |
108 |
if (td->socklist[i].handler.sock.outbuf != NULL) |
109 |
tot += td->socklist[i].handler.sock.outbuflen; |
110 |
} |
111 |
} |
112 |
return tot; |
113 |
} |
114 |
|
115 |
/* Extract the IP address from a sockaddr struct and convert it |
116 |
* to presentation format. |
117 |
*/ |
118 |
char *iptostr(struct sockaddr *sa) |
119 |
{ |
120 |
#ifdef IPV6 |
121 |
static char s[INET6_ADDRSTRLEN] = ""; |
122 |
if (sa->sa_family == AF_INET6) |
123 |
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)sa)->sin6_addr, |
124 |
s, sizeof s); |
125 |
else |
126 |
#else |
127 |
static char s[sizeof "255.255.255.255"] = ""; |
128 |
#endif |
129 |
inet_ntop(AF_INET, &((struct sockaddr_in *)sa)->sin_addr.s_addr, s, |
130 |
sizeof s); |
131 |
return s; |
132 |
} |
133 |
|
134 |
/* Fills in a sockname struct with the given server and port. If the string |
135 |
* pointed by src isn't an IP address and allowres is not null, the function |
136 |
* will assume it's a hostname and will attempt to resolve it. This is |
137 |
* convenient, but you should use the async dns functions where possible, to |
138 |
* avoid blocking the bot while the lookup is performed. |
139 |
*/ |
140 |
int setsockname(sockname_t *addr, char *src, int port, int allowres) |
141 |
{ |
142 |
struct hostent *hp; |
143 |
int af = AF_UNSPEC; |
144 |
#ifdef IPV6 |
145 |
int pref; |
146 |
|
147 |
/* Clean start */ |
148 |
egg_bzero(addr, sizeof(sockname_t)); |
149 |
af = pref = pref_af ? AF_INET6 : AF_INET; |
150 |
if (pref == AF_INET) { |
151 |
if (!egg_inet_aton(src, &addr->addr.s4.sin_addr)) |
152 |
af = AF_INET6; |
153 |
} else { |
154 |
if (inet_pton(af, src, &addr->addr.s6.sin6_addr) != 1) |
155 |
af = AF_INET; |
156 |
} |
157 |
if (af != pref) |
158 |
if (((af == AF_INET6) && |
159 |
(inet_pton(af, src, &addr->addr.s6.sin6_addr) != 1)) || |
160 |
((af == AF_INET) && |
161 |
!egg_inet_aton(src, &addr->addr.s4.sin_addr))) |
162 |
af = AF_UNSPEC; |
163 |
|
164 |
if (af == AF_UNSPEC && allowres) { |
165 |
/* src is a hostname. Attempt to resolve it.. */ |
166 |
if (!sigsetjmp(alarmret, 1)) { |
167 |
alarm(resolve_timeout); |
168 |
hp = gethostbyname2(src, pref_af ? AF_INET6 : AF_INET); |
169 |
if (!hp) |
170 |
hp = gethostbyname2(src, pref_af ? AF_INET : AF_INET6); |
171 |
alarm(0); |
172 |
} else |
173 |
hp = NULL; |
174 |
if (hp) { |
175 |
if (hp->h_addrtype == AF_INET) |
176 |
egg_memcpy(&addr->addr.s4.sin_addr, hp->h_addr, hp->h_length); |
177 |
else |
178 |
egg_memcpy(&addr->addr.s6.sin6_addr, hp->h_addr, hp->h_length); |
179 |
af = hp->h_addrtype; |
180 |
} |
181 |
} |
182 |
|
183 |
addr->family = (af == AF_UNSPEC) ? pref : af; |
184 |
addr->addr.sa.sa_family = addr->family; |
185 |
if (addr->family == AF_INET6) { |
186 |
addr->addrlen = sizeof(struct sockaddr_in6); |
187 |
addr->addr.s6.sin6_port = htons(port); |
188 |
addr->addr.s6.sin6_family = AF_INET6; |
189 |
} else { |
190 |
addr->addrlen = sizeof(struct sockaddr_in); |
191 |
addr->addr.s4.sin_port = htons(port); |
192 |
addr->addr.s4.sin_family = AF_INET; |
193 |
} |
194 |
#else |
195 |
egg_bzero(addr, sizeof(sockname_t)); |
196 |
if (!egg_inet_aton(src, &addr->addr.s4.sin_addr) && allowres) { |
197 |
/* src is a hostname. Attempt to resolve it.. */ |
198 |
if (!sigsetjmp(alarmret, 1)) { |
199 |
alarm(resolve_timeout); |
200 |
hp = gethostbyname(src); |
201 |
alarm(0); |
202 |
} else |
203 |
hp = NULL; |
204 |
if (hp) { |
205 |
egg_memcpy(&addr->addr.s4.sin_addr, hp->h_addr, hp->h_length); |
206 |
af = hp->h_addrtype; |
207 |
} |
208 |
} else |
209 |
af = AF_INET; |
210 |
|
211 |
addr->family = addr->addr.s4.sin_family = AF_INET; |
212 |
addr->addr.sa.sa_family = addr->family; |
213 |
addr->addrlen = sizeof(struct sockaddr_in); |
214 |
addr->addr.s4.sin_port = htons(port); |
215 |
#endif |
216 |
return af; |
217 |
} |
218 |
|
219 |
/* Get socket address to bind to for outbound connections |
220 |
*/ |
221 |
void getvhost(sockname_t *addr, int af) |
222 |
{ |
223 |
char *h; |
224 |
|
225 |
if (af == AF_INET) |
226 |
h = vhost; |
227 |
#ifdef IPV6 |
228 |
else |
229 |
h = vhost6; |
230 |
#endif |
231 |
if (setsockname(addr, h, 0, 1) != af) |
232 |
setsockname(addr, (af == AF_INET ? "0" : "::"), 0, 0); |
233 |
/* Remember this 'self-lookup failed' thingie? |
234 |
I have good news - you won't see it again ;) */ |
235 |
} |
236 |
|
237 |
/* Sets/Unsets options for a specific socket. |
238 |
* |
239 |
* Returns: 0 - on success |
240 |
* -1 - socket not found |
241 |
* -2 - illegal operation |
242 |
*/ |
243 |
int sockoptions(int sock, int operation, int sock_options) |
244 |
{ |
245 |
int i; |
246 |
struct threaddata *td = threaddata(); |
247 |
|
248 |
for (i = 0; i < td->MAXSOCKS; i++) |
249 |
if ((td->socklist[i].sock == sock) && |
250 |
!(td->socklist[i].flags & SOCK_UNUSED)) { |
251 |
if (operation == EGG_OPTION_SET) |
252 |
td->socklist[i].flags |= sock_options; |
253 |
else if (operation == EGG_OPTION_UNSET) |
254 |
td->socklist[i].flags &= ~sock_options; |
255 |
else |
256 |
return -2; |
257 |
return 0; |
258 |
} |
259 |
return -1; |
260 |
} |
261 |
|
262 |
/* Return a free entry in the socket entry |
263 |
*/ |
264 |
int allocsock(int sock, int options) |
265 |
{ |
266 |
int i; |
267 |
struct threaddata *td = threaddata(); |
268 |
|
269 |
for (i = 0; i < td->MAXSOCKS; i++) { |
270 |
if (td->socklist[i].flags & SOCK_UNUSED) { |
271 |
/* yay! there is table space */ |
272 |
td->socklist[i].handler.sock.inbuf = NULL; |
273 |
td->socklist[i].handler.sock.outbuf = NULL; |
274 |
td->socklist[i].handler.sock.inbuflen = 0; |
275 |
td->socklist[i].handler.sock.outbuflen = 0; |
276 |
td->socklist[i].flags = options; |
277 |
td->socklist[i].sock = sock; |
278 |
#ifdef TLS |
279 |
td->socklist[i].ssl = 0; |
280 |
#endif |
281 |
return i; |
282 |
} |
283 |
} |
284 |
/* Try again if enlarging socketlist works */ |
285 |
if (increase_socks_max()) |
286 |
return -1; |
287 |
else |
288 |
return allocsock(sock, options); |
289 |
} |
290 |
|
291 |
/* Return a free entry in the socket entry for a tcl socket |
292 |
* |
293 |
* alloctclsock() can be called by Tcl threads |
294 |
*/ |
295 |
int alloctclsock(register int sock, int mask, Tcl_FileProc *proc, ClientData cd) |
296 |
{ |
297 |
int f = -1; |
298 |
register int i; |
299 |
struct threaddata *td = threaddata(); |
300 |
|
301 |
for (i = 0; i < td->MAXSOCKS; i++) { |
302 |
if (td->socklist[i].flags & SOCK_UNUSED) { |
303 |
if (f == -1) |
304 |
f = i; |
305 |
} else if ((td->socklist[i].flags & SOCK_TCL) && |
306 |
td->socklist[i].sock == sock) { |
307 |
f = i; |
308 |
break; |
309 |
} |
310 |
} |
311 |
if (f != -1) { |
312 |
td->socklist[f].sock = sock; |
313 |
td->socklist[f].flags = SOCK_TCL; |
314 |
td->socklist[f].handler.tclsock.mask = mask; |
315 |
td->socklist[f].handler.tclsock.proc = proc; |
316 |
td->socklist[f].handler.tclsock.cd = cd; |
317 |
return f; |
318 |
} |
319 |
/* Try again if enlarging socketlist works */ |
320 |
if (increase_socks_max()) |
321 |
return -1; |
322 |
else |
323 |
return alloctclsock(sock, mask, proc, cd); |
324 |
} |
325 |
|
326 |
/* Request a normal socket for i/o |
327 |
*/ |
328 |
void setsock(int sock, int options) |
329 |
{ |
330 |
int i = allocsock(sock, options), parm; |
331 |
struct threaddata *td = threaddata(); |
332 |
|
333 |
if (i == -1) { |
334 |
putlog(LOG_MISC, "*", "Sockettable full."); |
335 |
return; |
336 |
} |
337 |
if (((sock != STDOUT) || backgrd) && !(td->socklist[i].flags & SOCK_NONSOCK)) { |
338 |
parm = 1; |
339 |
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &parm, sizeof(int)); |
340 |
|
341 |
parm = 0; |
342 |
setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &parm, sizeof(int)); |
343 |
} |
344 |
if (options & SOCK_LISTEN) { |
345 |
/* Tris says this lets us grab the same port again next time */ |
346 |
parm = 1; |
347 |
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &parm, sizeof(int)); |
348 |
} |
349 |
/* Yay async i/o ! */ |
350 |
fcntl(sock, F_SETFL, O_NONBLOCK); |
351 |
} |
352 |
|
353 |
int getsock(int af, int options) |
354 |
{ |
355 |
int sock = socket(af, SOCK_STREAM, 0); |
356 |
|
357 |
if (sock >= 0) |
358 |
setsock(sock, options); |
359 |
else |
360 |
putlog(LOG_MISC, "*", "Warning: Can't create new socket: %s!", |
361 |
strerror(errno)); |
362 |
return sock; |
363 |
} |
364 |
|
365 |
/* Done with a socket |
366 |
*/ |
367 |
void killsock(register int sock) |
368 |
{ |
369 |
register int i; |
370 |
struct threaddata *td = threaddata(); |
371 |
|
372 |
/* Ignore invalid sockets. */ |
373 |
if (sock < 0) |
374 |
return; |
375 |
|
376 |
for (i = 0; i < td->MAXSOCKS; i++) { |
377 |
if ((td->socklist[i].sock == sock) && !(td->socklist[i].flags & SOCK_UNUSED)) { |
378 |
if (!(td->socklist[i].flags & SOCK_TCL)) { /* nothing to free for tclsocks */ |
379 |
#ifdef TLS |
380 |
if (td->socklist[i].ssl) { |
381 |
SSL_shutdown(td->socklist[i].ssl); |
382 |
nfree(SSL_get_app_data(td->socklist[i].ssl)); |
383 |
SSL_free(td->socklist[i].ssl); |
384 |
td->socklist[i].ssl = NULL; |
385 |
} |
386 |
#endif |
387 |
close(td->socklist[i].sock); |
388 |
if (td->socklist[i].handler.sock.inbuf != NULL) { |
389 |
nfree(td->socklist[i].handler.sock.inbuf); |
390 |
td->socklist[i].handler.sock.inbuf = NULL; |
391 |
} |
392 |
if (td->socklist[i].handler.sock.outbuf != NULL) { |
393 |
nfree(td->socklist[i].handler.sock.outbuf); |
394 |
td->socklist[i].handler.sock.outbuf = NULL; |
395 |
td->socklist[i].handler.sock.outbuflen = 0; |
396 |
} |
397 |
} |
398 |
td->socklist[i].flags = SOCK_UNUSED; |
399 |
return; |
400 |
} |
401 |
} |
402 |
putlog(LOG_MISC, "*", "Warning: Attempt to kill un-allocated socket %d!", sock); |
403 |
} |
404 |
|
405 |
/* Done with a tcl socket |
406 |
* |
407 |
* killtclsock() can be called by Tcl threads |
408 |
*/ |
409 |
void killtclsock(register int sock) |
410 |
{ |
411 |
register int i; |
412 |
struct threaddata *td = threaddata(); |
413 |
|
414 |
if (sock < 0) |
415 |
return; |
416 |
|
417 |
for (i = 0; i < td->MAXSOCKS; i++) { |
418 |
if ((td->socklist[i].flags & SOCK_TCL) && td->socklist[i].sock == sock) { |
419 |
td->socklist[i].flags = SOCK_UNUSED; |
420 |
return; |
421 |
} |
422 |
} |
423 |
} |
424 |
|
425 |
/* Send connection request to proxy |
426 |
*/ |
427 |
static int proxy_connect(int sock, sockname_t *addr) |
428 |
{ |
429 |
sockname_t name; |
430 |
char host[121], s[256]; |
431 |
int i, port, proxy; |
432 |
|
433 |
if (!firewall[0]) |
434 |
return -2; |
435 |
#ifdef IPV6 |
436 |
if (addr->family == AF_INET6) { |
437 |
putlog(LOG_MISC, "*", "Eggdrop doesn't support IPv6 connections " |
438 |
"through proxies yet."); |
439 |
return -1; |
440 |
} |
441 |
#endif |
442 |
if (firewall[0] == '!') { |
443 |
proxy = PROXY_SUN; |
444 |
strcpy(host, &firewall[1]); |
445 |
} else { |
446 |
proxy = PROXY_SOCKS; |
447 |
strcpy(host, firewall); |
448 |
} |
449 |
port = addr->addr.s4.sin_port; |
450 |
setsockname(&name, host, firewallport, 1); |
451 |
if (connect(sock, &name.addr.sa, name.addrlen) < 0 && errno != EINPROGRESS) |
452 |
return -1; |
453 |
if (proxy == PROXY_SOCKS) { |
454 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) |
455 |
if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock) |
456 |
socklist[i].flags |= SOCK_PROXYWAIT; /* drummer */ |
457 |
egg_memcpy(host, &addr->addr.s4.sin_addr.s_addr, 4); |
458 |
egg_snprintf(s, sizeof s, "\004\001%c%c%c%c%c%c%s", port % 256, |
459 |
(port >> 8) % 256, host[0], host[1], host[2], host[3], botuser); |
460 |
tputs(sock, s, strlen(botuser) + 9); /* drummer */ |
461 |
} else if (proxy == PROXY_SUN) { |
462 |
inet_ntop(AF_INET, &addr->addr.s4.sin_addr, host, sizeof host); |
463 |
egg_snprintf(s, sizeof s, "%s %d\n", host, port); |
464 |
tputs(sock, s, strlen(s)); /* drummer */ |
465 |
} |
466 |
return sock; |
467 |
} |
468 |
|
469 |
/* Starts a connection attempt through a socket |
470 |
* |
471 |
* The server address should be filled in addr by setsockname() or by the |
472 |
* non-blocking dns functions and setsnport(). |
473 |
* |
474 |
* returns < 0 if connection refused: |
475 |
* -1 strerror() type error |
476 |
*/ |
477 |
int open_telnet_raw(int sock, sockname_t *addr) |
478 |
{ |
479 |
sockname_t name; |
480 |
int i, rc; |
481 |
|
482 |
getvhost(&name, addr->family); |
483 |
if (bind(sock, &name.addr.sa, name.addrlen) < 0) { |
484 |
return -1; |
485 |
} |
486 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
487 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == sock)) |
488 |
socklist[i].flags = (socklist[i].flags & ~SOCK_VIRTUAL) | SOCK_CONNECT; |
489 |
} |
490 |
if (addr->family == AF_INET && firewall[0]) |
491 |
return proxy_connect(sock, addr); |
492 |
rc = connect(sock, &addr->addr.sa, addr->addrlen); |
493 |
if (rc < 0) { |
494 |
if (errno == EINPROGRESS) |
495 |
return sock; /* async success! */ |
496 |
else |
497 |
return -1; |
498 |
} |
499 |
return sock; |
500 |
} |
501 |
|
502 |
/* Ordinary non-binary connection attempt |
503 |
* Return values: |
504 |
* >=0: connect successful, returned is the socket number |
505 |
* -1: look at errno or use strerror() |
506 |
* -2: lookup failed or server is not a valid IP string |
507 |
*/ |
508 |
int open_telnet(char *server, int port) |
509 |
{ |
510 |
int ret, sock; |
511 |
sockname_t name; |
512 |
|
513 |
ret = setsockname(&name, server, port, 1); |
514 |
if (ret == AF_UNSPEC) |
515 |
return -2; |
516 |
sock = getsock(ret, 0); |
517 |
if (sock < 0) |
518 |
return -1; |
519 |
ret = open_telnet_raw(sock, &name); |
520 |
if (ret < 0) |
521 |
killsock(sock); |
522 |
return ret; |
523 |
} |
524 |
|
525 |
/* Returns a socket number for a listening socket that will accept any |
526 |
* connection on the given address. The address can be filled in by |
527 |
* setsockname(). |
528 |
*/ |
529 |
int open_address_listen(sockname_t *addr) |
530 |
{ |
531 |
int sock = 0; |
532 |
|
533 |
sock = getsock(addr->family, SOCK_LISTEN); |
534 |
if (sock < 0) |
535 |
return -1; |
536 |
#if defined IPV6 && IPV6_V6ONLY |
537 |
if (addr->family == AF_INET6) { |
538 |
int on = 0; |
539 |
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof(on)); |
540 |
} |
541 |
#endif |
542 |
if (bind(sock, &addr->addr.sa, addr->addrlen) < 0) { |
543 |
killsock(sock); |
544 |
return -2; |
545 |
} |
546 |
|
547 |
if (getsockname(sock, &addr->addr.sa, &addr->addrlen) < 0) { |
548 |
killsock(sock); |
549 |
return -1; |
550 |
} |
551 |
if (listen(sock, 1) < 0) { |
552 |
killsock(sock); |
553 |
return -1; |
554 |
} |
555 |
|
556 |
return sock; |
557 |
} |
558 |
|
559 |
/* Returns a socket number for a listening socket that will accept any |
560 |
* connection -- port # is returned in port |
561 |
*/ |
562 |
inline int open_listen(int *port) |
563 |
{ |
564 |
int sock; |
565 |
sockname_t name; |
566 |
|
567 |
(void) setsockname(&name, listen_ip, *port, 1); |
568 |
sock = open_address_listen(&name); |
569 |
if (name.addr.sa.sa_family == AF_INET) |
570 |
*port = ntohs(name.addr.s4.sin_port); |
571 |
#ifdef IPV6 |
572 |
else |
573 |
*port = ntohs(name.addr.s6.sin6_port); |
574 |
#endif |
575 |
return sock; |
576 |
} |
577 |
|
578 |
/* Short routine to answer a connect received on a listening socket. |
579 |
* Returned is the new socket. |
580 |
* If port is not NULL, it points to an integer to hold the port number |
581 |
* of the caller. |
582 |
*/ |
583 |
int answer(int sock, sockname_t *caller, unsigned short *port, int binary) |
584 |
{ |
585 |
int new_sock; |
586 |
caller->addrlen = sizeof(caller->addr); |
587 |
new_sock = accept(sock, &caller->addr.sa, &caller->addrlen); |
588 |
|
589 |
if (new_sock < 0) |
590 |
return -1; |
591 |
|
592 |
caller->family = caller->addr.sa.sa_family; |
593 |
if (port) { |
594 |
if (caller->family == AF_INET) |
595 |
*port = ntohs(caller->addr.s4.sin_port); |
596 |
#ifdef IPV6 |
597 |
else |
598 |
*port = ntohs(caller->addr.s6.sin6_port); |
599 |
#endif |
600 |
} |
601 |
setsock(new_sock, (binary ? SOCK_BINARY : 0)); |
602 |
return new_sock; |
603 |
} |
604 |
|
605 |
/* Get DCC compatible address for a client to connect (e.g. 1660944385) |
606 |
* If addr is not NULL, it should point to the listening socket's address. |
607 |
* Otherwise, this function will try to figure out the public address of the |
608 |
* machine, using listen_ip and natip. |
609 |
* The result is a string useable for DCC requests |
610 |
*/ |
611 |
int getdccaddr(sockname_t *addr, char *s, size_t l) |
612 |
{ |
613 |
char h[121]; |
614 |
sockname_t name, *r = &name; |
615 |
|
616 |
if (addr) |
617 |
r = addr; |
618 |
else |
619 |
setsockname(r, listen_ip, 0, 1); |
620 |
if ( |
621 |
#ifdef IPV6 |
622 |
((r->family == AF_INET6) && |
623 |
IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) || |
624 |
#endif |
625 |
(r->family == AF_INET && !r->addr.s4.sin_addr.s_addr)) { |
626 |
/* We can't send :: or 0.0.0.0 for dcc, so try |
627 |
to figure out some real address */ |
628 |
r = &name; |
629 |
gethostname(h, sizeof h); |
630 |
setsockname(r, h, 0, 1); |
631 |
} |
632 |
|
633 |
if ( |
634 |
#ifdef IPV6 |
635 |
((r->family == AF_INET6) && |
636 |
IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) || |
637 |
#endif |
638 |
(!natip[0] && (r->family == AF_INET) && !r->addr.s4.sin_addr.s_addr)) |
639 |
return 0; |
640 |
|
641 |
#ifdef IPV6 |
642 |
if (r->family == AF_INET6) { |
643 |
if (IN6_IS_ADDR_V4MAPPED(&r->addr.s6.sin6_addr) || |
644 |
IN6_IS_ADDR_UNSPECIFIED(&r->addr.s6.sin6_addr)) |
645 |
snprintf(s, l, "%lu", natip[0] ? iptolong(inet_addr(natip)) : |
646 |
ntohl(*(IP *) &r->addr.s6.sin6_addr.s6_addr[12])); |
647 |
else |
648 |
inet_ntop(AF_INET6, &r->addr.s6.sin6_addr, s, l); |
649 |
} else |
650 |
#endif |
651 |
snprintf(s, l, "%lu", natip[0] ? iptolong(inet_addr(natip)) : |
652 |
ntohl(r->addr.s4.sin_addr.s_addr)); |
653 |
return 1; |
654 |
} |
655 |
|
656 |
/* Builds the fd_sets for select(). Eggdrop only cares about readable |
657 |
* sockets, but tcl also cares for writable/exceptions. |
658 |
* preparefdset() can be called by Tcl Threads |
659 |
*/ |
660 |
int preparefdset(fd_set *fd, sock_list *slist, int slistmax, int tclonly, int tclmask) |
661 |
{ |
662 |
int fdtmp, i, foundsocks = 0; |
663 |
|
664 |
FD_ZERO(fd); |
665 |
for (i = 0; i < slistmax; i++) { |
666 |
if (!(slist[i].flags & (SOCK_UNUSED | SOCK_VIRTUAL))) { |
667 |
if ((slist[i].sock == STDOUT) && !backgrd) |
668 |
fdtmp = STDIN; |
669 |
else |
670 |
fdtmp = slist[i].sock; |
671 |
/* |
672 |
* Looks like that having more than a call, in the same |
673 |
* program, to the FD_SET macro, triggers a bug in gcc. |
674 |
* SIGBUS crashing binaries used to be produced on a number |
675 |
* (prolly all?) of 64 bits architectures. |
676 |
* Make your best to avoid to make it happen again. |
677 |
* |
678 |
* ITE |
679 |
*/ |
680 |
if (slist[i].flags & SOCK_TCL) { |
681 |
if (!(slist[i].handler.tclsock.mask & tclmask)) |
682 |
continue; |
683 |
} else if (tclonly) |
684 |
continue; |
685 |
foundsocks = 1; |
686 |
FD_SET(fdtmp, fd); |
687 |
} |
688 |
} |
689 |
return foundsocks; |
690 |
} |
691 |
|
692 |
/* Attempts to read from all sockets in slist (upper array boundary slistmax-1) |
693 |
* fills s with up to 511 bytes if available, and returns the array index |
694 |
* Also calls all handler procs for Tcl sockets |
695 |
* sockread() can be called by Tcl threads |
696 |
* |
697 |
* on EOF: returns -1, with socket in len |
698 |
* on socket error: returns -2 |
699 |
* if nothing is ready: returns -3 |
700 |
* tcl sockets busy: returns -5 |
701 |
*/ |
702 |
int sockread(char *s, int *len, sock_list *slist, int slistmax, int tclonly) |
703 |
{ |
704 |
struct timeval t; |
705 |
fd_set fdr, fdw, fde; |
706 |
int fds, i, x, have_r, have_w, have_e; |
707 |
int grab = 511, tclsock = -1, events = 0; |
708 |
struct threaddata *td = threaddata(); |
709 |
|
710 |
fds = getdtablesize(); |
711 |
#ifdef FD_SETSIZE |
712 |
if (fds > FD_SETSIZE) |
713 |
fds = FD_SETSIZE; /* Fixes YET ANOTHER freebsd bug!!! */ |
714 |
#endif |
715 |
|
716 |
have_r = preparefdset(&fdr, slist, slistmax, tclonly, TCL_READABLE); |
717 |
have_w = preparefdset(&fdw, slist, slistmax, 1, TCL_WRITABLE); |
718 |
have_e = preparefdset(&fde, slist, slistmax, 1, TCL_EXCEPTION); |
719 |
|
720 |
/* select() may modify the timeval argument - copy it */ |
721 |
t.tv_sec = td->blocktime.tv_sec; |
722 |
t.tv_usec = td->blocktime.tv_usec; |
723 |
|
724 |
x = select((SELECT_TYPE_ARG1) fds, |
725 |
SELECT_TYPE_ARG234 (have_r ? &fdr : NULL), |
726 |
SELECT_TYPE_ARG234 (have_w ? &fdw : NULL), |
727 |
SELECT_TYPE_ARG234 (have_e ? &fde : NULL), |
728 |
SELECT_TYPE_ARG5 &t); |
729 |
if (x > 0) { |
730 |
/* Something happened */ |
731 |
for (i = 0; i < slistmax; i++) { |
732 |
if (!tclonly && ((!(slist[i].flags & (SOCK_UNUSED | SOCK_TCL))) && |
733 |
((FD_ISSET(slist[i].sock, &fdr)) || |
734 |
#ifdef TLS |
735 |
(slist[i].ssl && SSL_pending(slist[i].ssl)) || |
736 |
#endif |
737 |
((slist[i].sock == STDOUT) && (!backgrd) && |
738 |
(FD_ISSET(STDIN, &fdr)))))) { |
739 |
if (slist[i].flags & (SOCK_LISTEN | SOCK_CONNECT)) { |
740 |
/* Listening socket -- don't read, just return activity */ |
741 |
/* Same for connection attempt */ |
742 |
/* (for strong connections, require a read to succeed first) */ |
743 |
if (slist[i].flags & SOCK_PROXYWAIT) /* drummer */ |
744 |
/* Hang around to get the return code from proxy */ |
745 |
grab = 10; |
746 |
#ifdef TLS |
747 |
else if (!(slist[i].flags & SOCK_STRONGCONN) && |
748 |
(!(slist[i].ssl) || !SSL_in_init(slist[i].ssl))) { |
749 |
#else |
750 |
else if (!(slist[i].flags & SOCK_STRONGCONN)) { |
751 |
#endif |
752 |
debug1("net: connect! sock %d", slist[i].sock); |
753 |
s[0] = 0; |
754 |
*len = 0; |
755 |
return i; |
756 |
} |
757 |
} else if (slist[i].flags & SOCK_PASS) { |
758 |
s[0] = 0; |
759 |
*len = 0; |
760 |
return i; |
761 |
} |
762 |
errno = 0; |
763 |
if ((slist[i].sock == STDOUT) && !backgrd) |
764 |
x = read(STDIN, s, grab); |
765 |
else |
766 |
#ifdef TLS |
767 |
{ |
768 |
if (slist[i].ssl) { |
769 |
x = SSL_read(slist[i].ssl, s, grab); |
770 |
if (x < 0) { |
771 |
int err = SSL_get_error(slist[i].ssl, x); |
772 |
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) |
773 |
errno = EAGAIN; |
774 |
else |
775 |
debug1("SSL error: %s", ERR_error_string(ERR_get_error(), 0)); |
776 |
x = -1; |
777 |
} |
778 |
} else |
779 |
x = read(slist[i].sock, s, grab); |
780 |
} |
781 |
#else |
782 |
x = read(slist[i].sock, s, grab); |
783 |
#endif |
784 |
if (x <= 0) { /* eof */ |
785 |
if (errno != EAGAIN) { /* EAGAIN happens when the operation would |
786 |
* block on a non-blocking socket, if the |
787 |
* socket is going to die, it will die later, |
788 |
* otherwise it will connect. */ |
789 |
*len = slist[i].sock; |
790 |
slist[i].flags &= ~SOCK_CONNECT; |
791 |
debug1("net: eof!(read) socket %d", slist[i].sock); |
792 |
return -1; |
793 |
} else { |
794 |
debug3("sockread EAGAIN: %d %d (%s)", slist[i].sock, errno, |
795 |
strerror(errno)); |
796 |
continue; /* EAGAIN */ |
797 |
} |
798 |
} |
799 |
s[x] = 0; |
800 |
*len = x; |
801 |
if (slist[i].flags & SOCK_PROXYWAIT) { |
802 |
debug2("net: socket: %d proxy errno: %d", slist[i].sock, s[1]); |
803 |
slist[i].flags &= ~(SOCK_CONNECT | SOCK_PROXYWAIT); |
804 |
switch (s[1]) { |
805 |
case 90: /* Success */ |
806 |
s[0] = 0; |
807 |
*len = 0; |
808 |
return i; |
809 |
case 91: /* Failed */ |
810 |
errno = ECONNREFUSED; |
811 |
break; |
812 |
case 92: /* No identd */ |
813 |
case 93: /* Identd said wrong username */ |
814 |
/* A better error message would be "socks misconfigured" |
815 |
* or "identd not working" but this is simplest. |
816 |
*/ |
817 |
errno = ENETUNREACH; |
818 |
break; |
819 |
} |
820 |
*len = slist[i].sock; |
821 |
return -1; |
822 |
} |
823 |
return i; |
824 |
} else if (tclsock == -1 && (slist[i].flags & SOCK_TCL)) { |
825 |
events = FD_ISSET(slist[i].sock, &fdr) ? TCL_READABLE : 0; |
826 |
events |= FD_ISSET(slist[i].sock, &fdw) ? TCL_WRITABLE : 0; |
827 |
events |= FD_ISSET(slist[i].sock, &fde) ? TCL_EXCEPTION : 0; |
828 |
events &= slist[i].handler.tclsock.mask; |
829 |
if (events) |
830 |
tclsock = i; |
831 |
} |
832 |
} |
833 |
} else if (x == -1) |
834 |
return -2; /* socket error */ |
835 |
else if (!tclonly) { |
836 |
s[0] = 0; |
837 |
*len = 0; |
838 |
} |
839 |
if (tclsock != -1) { |
840 |
(*slist[tclsock].handler.tclsock.proc)(slist[tclsock].handler.tclsock.cd, |
841 |
events); |
842 |
return -5; |
843 |
} |
844 |
return -3; |
845 |
} |
846 |
|
847 |
/* sockgets: buffer and read from sockets |
848 |
* |
849 |
* Attempts to read from all registered sockets for up to one second. if |
850 |
* after one second, no complete data has been received from any of the |
851 |
* sockets, 's' will be empty, 'len' will be 0, and sockgets will return -3. |
852 |
* if there is returnable data received from a socket, the data will be |
853 |
* in 's' (null-terminated if non-binary), the length will be returned |
854 |
* in len, and the socket number will be returned. |
855 |
* normal sockets have their input buffered, and each call to sockgets |
856 |
* will return one line terminated with a '\n'. binary sockets are not |
857 |
* buffered and return whatever coems in as soon as it arrives. |
858 |
* listening sockets will return an empty string when a connection comes in. |
859 |
* connecting sockets will return an empty string on a successful connect, |
860 |
* or EOF on a failed connect. |
861 |
* if an EOF is detected from any of the sockets, that socket number will be |
862 |
* put in len, and -1 will be returned. |
863 |
* the maximum length of the string returned is 512 (including null) |
864 |
* |
865 |
* Returns -4 if we handled something that shouldn't be handled by the |
866 |
* dcc functions. Simply ignore it. |
867 |
* Returns -5 if tcl sockets are busy but not eggdrop sockets. |
868 |
*/ |
869 |
|
870 |
int sockgets(char *s, int *len) |
871 |
{ |
872 |
char xx[514], *p, *px; |
873 |
int ret, i, data = 0; |
874 |
|
875 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
876 |
/* Check for stored-up data waiting to be processed */ |
877 |
if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL | SOCK_BUFFER)) && |
878 |
(socklist[i].handler.sock.inbuf != NULL)) { |
879 |
if (!(socklist[i].flags & SOCK_BINARY)) { |
880 |
/* look for \r too cos windows can't follow RFCs */ |
881 |
p = strchr(socklist[i].handler.sock.inbuf, '\n'); |
882 |
if (p == NULL) |
883 |
p = strchr(socklist[i].handler.sock.inbuf, '\r'); |
884 |
if (p != NULL) { |
885 |
*p = 0; |
886 |
if (strlen(socklist[i].handler.sock.inbuf) > 510) |
887 |
socklist[i].handler.sock.inbuf[510] = 0; |
888 |
strcpy(s, socklist[i].handler.sock.inbuf); |
889 |
px = nmalloc(strlen(p + 1) + 1); |
890 |
strcpy(px, p + 1); |
891 |
nfree(socklist[i].handler.sock.inbuf); |
892 |
if (px[0]) |
893 |
socklist[i].handler.sock.inbuf = px; |
894 |
else { |
895 |
nfree(px); |
896 |
socklist[i].handler.sock.inbuf = NULL; |
897 |
} |
898 |
/* Strip CR if this was CR/LF combo */ |
899 |
if (s[strlen(s) - 1] == '\r') |
900 |
s[strlen(s) - 1] = 0; |
901 |
*len = strlen(s); |
902 |
return socklist[i].sock; |
903 |
} |
904 |
} else { |
905 |
/* Handling buffered binary data (must have been SOCK_BUFFER before). */ |
906 |
if (socklist[i].handler.sock.inbuflen <= 510) { |
907 |
*len = socklist[i].handler.sock.inbuflen; |
908 |
egg_memcpy(s, socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuflen); |
909 |
nfree(socklist[i].handler.sock.inbuf); |
910 |
socklist[i].handler.sock.inbuf = NULL; |
911 |
socklist[i].handler.sock.inbuflen = 0; |
912 |
} else { |
913 |
/* Split up into chunks of 510 bytes. */ |
914 |
*len = 510; |
915 |
egg_memcpy(s, socklist[i].handler.sock.inbuf, *len); |
916 |
egg_memcpy(socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuf + *len, *len); |
917 |
socklist[i].handler.sock.inbuflen -= *len; |
918 |
socklist[i].handler.sock.inbuf = nrealloc(socklist[i].handler.sock.inbuf, socklist[i].handler.sock.inbuflen); |
919 |
} |
920 |
return socklist[i].sock; |
921 |
} |
922 |
} |
923 |
/* Also check any sockets that might have EOF'd during write */ |
924 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].flags & SOCK_EOFD)) { |
925 |
s[0] = 0; |
926 |
*len = socklist[i].sock; |
927 |
return -1; |
928 |
} |
929 |
} |
930 |
/* No pent-up data of any worth -- down to business */ |
931 |
*len = 0; |
932 |
ret = sockread(xx, len, socklist, threaddata()->MAXSOCKS, 0); |
933 |
if (ret < 0) { |
934 |
s[0] = 0; |
935 |
return ret; |
936 |
} |
937 |
/* Binary, listening and passed on sockets don't get buffered. */ |
938 |
if (socklist[ret].flags & SOCK_CONNECT) { |
939 |
if (socklist[ret].flags & SOCK_STRONGCONN) { |
940 |
socklist[ret].flags &= ~SOCK_STRONGCONN; |
941 |
/* Buffer any data that came in, for future read. */ |
942 |
socklist[ret].handler.sock.inbuflen = *len; |
943 |
socklist[ret].handler.sock.inbuf = nmalloc(*len + 1); |
944 |
/* It might be binary data. You never know. */ |
945 |
egg_memcpy(socklist[ret].handler.sock.inbuf, xx, *len); |
946 |
socklist[ret].handler.sock.inbuf[*len] = 0; |
947 |
} |
948 |
socklist[ret].flags &= ~SOCK_CONNECT; |
949 |
s[0] = 0; |
950 |
return socklist[ret].sock; |
951 |
} |
952 |
if (socklist[ret].flags & SOCK_BINARY) { |
953 |
egg_memcpy(s, xx, *len); |
954 |
return socklist[ret].sock; |
955 |
} |
956 |
if (socklist[ret].flags & (SOCK_LISTEN | SOCK_PASS | SOCK_TCL)) { |
957 |
s[0] = 0; /* for the dcc traffic counters in the mainloop */ |
958 |
return socklist[ret].sock; |
959 |
} |
960 |
if (socklist[ret].flags & SOCK_BUFFER) { |
961 |
socklist[ret].handler.sock.inbuf = (char *) nrealloc(socklist[ret].handler.sock.inbuf, |
962 |
socklist[ret].handler.sock.inbuflen + *len + 1); |
963 |
egg_memcpy(socklist[ret].handler.sock.inbuf + socklist[ret].handler.sock.inbuflen, xx, *len); |
964 |
socklist[ret].handler.sock.inbuflen += *len; |
965 |
/* We don't know whether it's binary data. Make sure normal strings |
966 |
* will be handled properly later on too. */ |
967 |
socklist[ret].handler.sock.inbuf[socklist[ret].handler.sock.inbuflen] = 0; |
968 |
return -4; /* Ignore this one. */ |
969 |
} |
970 |
/* Might be necessary to prepend stored-up data! */ |
971 |
if (socklist[ret].handler.sock.inbuf != NULL) { |
972 |
p = socklist[ret].handler.sock.inbuf; |
973 |
socklist[ret].handler.sock.inbuf = nmalloc(strlen(p) + strlen(xx) + 1); |
974 |
strcpy(socklist[ret].handler.sock.inbuf, p); |
975 |
strcat(socklist[ret].handler.sock.inbuf, xx); |
976 |
nfree(p); |
977 |
if (strlen(socklist[ret].handler.sock.inbuf) < 512) { |
978 |
strcpy(xx, socklist[ret].handler.sock.inbuf); |
979 |
nfree(socklist[ret].handler.sock.inbuf); |
980 |
socklist[ret].handler.sock.inbuf = NULL; |
981 |
socklist[ret].handler.sock.inbuflen = 0; |
982 |
} else { |
983 |
p = socklist[ret].handler.sock.inbuf; |
984 |
socklist[ret].handler.sock.inbuflen = strlen(p) - 510; |
985 |
socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); |
986 |
strcpy(socklist[ret].handler.sock.inbuf, p + 510); |
987 |
*(p + 510) = 0; |
988 |
strcpy(xx, p); |
989 |
nfree(p); |
990 |
/* (leave the rest to be post-pended later) */ |
991 |
} |
992 |
} |
993 |
/* Look for EOL marker; if it's there, i have something to show */ |
994 |
p = strchr(xx, '\n'); |
995 |
if (p == NULL) |
996 |
p = strchr(xx, '\r'); |
997 |
if (p != NULL) { |
998 |
*p = 0; |
999 |
strcpy(s, xx); |
1000 |
memmove(xx, p + 1, strlen(p + 1) + 1); |
1001 |
if (s[strlen(s) - 1] == '\r') |
1002 |
s[strlen(s) - 1] = 0; |
1003 |
data = 1; /* DCC_CHAT may now need to process a blank line */ |
1004 |
/* NO! */ |
1005 |
/* if (!s[0]) strcpy(s," "); */ |
1006 |
} else { |
1007 |
s[0] = 0; |
1008 |
if (strlen(xx) >= 510) { |
1009 |
/* String is too long, so just insert fake \n */ |
1010 |
strcpy(s, xx); |
1011 |
xx[0] = 0; |
1012 |
data = 1; |
1013 |
} |
1014 |
} |
1015 |
*len = strlen(s); |
1016 |
/* Anything left that needs to be saved? */ |
1017 |
if (!xx[0]) { |
1018 |
if (data) |
1019 |
return socklist[ret].sock; |
1020 |
else |
1021 |
return -3; |
1022 |
} |
1023 |
/* Prepend old data back */ |
1024 |
if (socklist[ret].handler.sock.inbuf != NULL) { |
1025 |
p = socklist[ret].handler.sock.inbuf; |
1026 |
socklist[ret].handler.sock.inbuflen = strlen(p) + strlen(xx); |
1027 |
socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); |
1028 |
strcpy(socklist[ret].handler.sock.inbuf, xx); |
1029 |
strcat(socklist[ret].handler.sock.inbuf, p); |
1030 |
nfree(p); |
1031 |
} else { |
1032 |
socklist[ret].handler.sock.inbuflen = strlen(xx); |
1033 |
socklist[ret].handler.sock.inbuf = nmalloc(socklist[ret].handler.sock.inbuflen + 1); |
1034 |
strcpy(socklist[ret].handler.sock.inbuf, xx); |
1035 |
} |
1036 |
if (data) |
1037 |
return socklist[ret].sock; |
1038 |
else |
1039 |
return -3; |
1040 |
} |
1041 |
|
1042 |
/* Dump something to a socket |
1043 |
* |
1044 |
* NOTE: Do NOT put Contexts in here if you want DEBUG to be meaningful!! |
1045 |
*/ |
1046 |
void tputs(register int z, char *s, unsigned int len) |
1047 |
{ |
1048 |
register int i, x, idx; |
1049 |
char *p; |
1050 |
static int inhere = 0; |
1051 |
|
1052 |
if (z < 0) /* um... HELLO?! sanity check please! */ |
1053 |
return; |
1054 |
|
1055 |
if (((z == STDOUT) || (z == STDERR)) && (!backgrd || use_stderr)) { |
1056 |
write(z, s, len); |
1057 |
return; |
1058 |
} |
1059 |
|
1060 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
1061 |
if (!(socklist[i].flags & SOCK_UNUSED) && (socklist[i].sock == z)) { |
1062 |
for (idx = 0; idx < dcc_total; idx++) { |
1063 |
if ((dcc[idx].sock == z) && dcc[idx].type && dcc[idx].type->name) { |
1064 |
if (!strncmp(dcc[idx].type->name, "BOT", 3)) |
1065 |
otraffic_bn_today += len; |
1066 |
else if (!strcmp(dcc[idx].type->name, "SERVER")) |
1067 |
otraffic_irc_today += len; |
1068 |
else if (!strncmp(dcc[idx].type->name, "CHAT", 4)) |
1069 |
otraffic_dcc_today += len; |
1070 |
else if (!strncmp(dcc[idx].type->name, "FILES", 5)) |
1071 |
otraffic_filesys_today += len; |
1072 |
else if (!strcmp(dcc[idx].type->name, "SEND")) |
1073 |
otraffic_trans_today += len; |
1074 |
else if (!strncmp(dcc[idx].type->name, "GET", 3)) |
1075 |
otraffic_trans_today += len; |
1076 |
else |
1077 |
otraffic_unknown_today += len; |
1078 |
break; |
1079 |
} |
1080 |
} |
1081 |
|
1082 |
if (socklist[i].handler.sock.outbuf != NULL) { |
1083 |
/* Already queueing: just add it */ |
1084 |
p = (char *) nrealloc(socklist[i].handler.sock.outbuf, socklist[i].handler.sock.outbuflen + len); |
1085 |
egg_memcpy(p + socklist[i].handler.sock.outbuflen, s, len); |
1086 |
socklist[i].handler.sock.outbuf = p; |
1087 |
socklist[i].handler.sock.outbuflen += len; |
1088 |
return; |
1089 |
} |
1090 |
#ifdef TLS |
1091 |
if (socklist[i].ssl) { |
1092 |
x = SSL_write(socklist[i].ssl, s, len); |
1093 |
if (x < 0) { |
1094 |
int err = SSL_get_error(socklist[i].ssl, x); |
1095 |
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) |
1096 |
errno = EAGAIN; |
1097 |
else if (!inhere) { /* Out there, somewhere */ |
1098 |
inhere = 1; |
1099 |
debug1("SSL error: %s", ERR_error_string(ERR_get_error(), 0)); |
1100 |
inhere = 0; |
1101 |
} |
1102 |
x = -1; |
1103 |
} |
1104 |
} else /* not ssl, use regular write() */ |
1105 |
#endif |
1106 |
/* Try. */ |
1107 |
x = write(z, s, len); |
1108 |
if (x == -1) |
1109 |
x = 0; |
1110 |
if (x < len) { |
1111 |
/* Socket is full, queue it */ |
1112 |
socklist[i].handler.sock.outbuf = nmalloc(len - x); |
1113 |
egg_memcpy(socklist[i].handler.sock.outbuf, &s[x], len - x); |
1114 |
socklist[i].handler.sock.outbuflen = len - x; |
1115 |
} |
1116 |
return; |
1117 |
} |
1118 |
} |
1119 |
/* Make sure we don't cause a crash by looping here */ |
1120 |
if (!inhere) { |
1121 |
inhere = 1; |
1122 |
|
1123 |
putlog(LOG_MISC, "*", "!!! writing to nonexistent socket: %d", z); |
1124 |
s[strlen(s) - 1] = 0; |
1125 |
putlog(LOG_MISC, "*", "!-> '%s'", s); |
1126 |
|
1127 |
inhere = 0; |
1128 |
} |
1129 |
} |
1130 |
|
1131 |
/* tputs might queue data for sockets, let's dump as much of it as |
1132 |
* possible. |
1133 |
*/ |
1134 |
void dequeue_sockets() |
1135 |
{ |
1136 |
int i, x; |
1137 |
|
1138 |
int z = 0, fds; |
1139 |
fd_set wfds; |
1140 |
struct timeval tv; |
1141 |
|
1142 |
/* ^-- start poptix test code, this should avoid writes to sockets not ready to be written to. */ |
1143 |
fds = getdtablesize(); |
1144 |
|
1145 |
#ifdef FD_SETSIZE |
1146 |
if (fds > FD_SETSIZE) |
1147 |
fds = FD_SETSIZE; /* Fixes YET ANOTHER freebsd bug!!! */ |
1148 |
#endif |
1149 |
FD_ZERO(&wfds); |
1150 |
tv.tv_sec = 0; |
1151 |
tv.tv_usec = 0; /* we only want to see if it's ready for writing, no need to actually wait.. */ |
1152 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
1153 |
if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL)) && |
1154 |
socklist[i].handler.sock.outbuf != NULL) { |
1155 |
FD_SET(socklist[i].sock, &wfds); |
1156 |
z = 1; |
1157 |
} |
1158 |
} |
1159 |
if (!z) |
1160 |
return; /* nothing to write */ |
1161 |
|
1162 |
select((SELECT_TYPE_ARG1) fds, SELECT_TYPE_ARG234 NULL, |
1163 |
SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL, |
1164 |
SELECT_TYPE_ARG5 &tv); |
1165 |
|
1166 |
/* end poptix */ |
1167 |
|
1168 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
1169 |
if (!(socklist[i].flags & (SOCK_UNUSED | SOCK_TCL)) && |
1170 |
(socklist[i].handler.sock.outbuf != NULL) && (FD_ISSET(socklist[i].sock, &wfds))) { |
1171 |
/* Trick tputs into doing the work */ |
1172 |
errno = 0; |
1173 |
#ifdef TLS |
1174 |
if (socklist[i].ssl) { |
1175 |
x = SSL_write(socklist[i].ssl, socklist[i].handler.sock.outbuf, |
1176 |
socklist[i].handler.sock.outbuflen); |
1177 |
if (x < 0) { |
1178 |
int err = SSL_get_error(socklist[i].ssl, x); |
1179 |
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) |
1180 |
errno = EAGAIN; |
1181 |
else |
1182 |
debug1("SSL error: %s", ERR_error_string(ERR_get_error(), 0)); |
1183 |
x = -1; |
1184 |
} |
1185 |
} else |
1186 |
#endif |
1187 |
x = write(socklist[i].sock, socklist[i].handler.sock.outbuf, socklist[i].handler.sock.outbuflen); |
1188 |
if ((x < 0) && (errno != EAGAIN) |
1189 |
#ifdef EBADSLT |
1190 |
&& (errno != EBADSLT) |
1191 |
#endif |
1192 |
#ifdef ENOTCONN |
1193 |
&& (errno != ENOTCONN) |
1194 |
#endif |
1195 |
) { |
1196 |
/* This detects an EOF during writing */ |
1197 |
debug3("net: eof!(write) socket %d (%s,%d)", socklist[i].sock, |
1198 |
strerror(errno), errno); |
1199 |
socklist[i].flags |= SOCK_EOFD; |
1200 |
} else if (x == socklist[i].handler.sock.outbuflen) { |
1201 |
/* If the whole buffer was sent, nuke it */ |
1202 |
nfree(socklist[i].handler.sock.outbuf); |
1203 |
socklist[i].handler.sock.outbuf = NULL; |
1204 |
socklist[i].handler.sock.outbuflen = 0; |
1205 |
} else if (x > 0) { |
1206 |
char *p = socklist[i].handler.sock.outbuf; |
1207 |
|
1208 |
/* This removes any sent bytes from the beginning of the buffer */ |
1209 |
socklist[i].handler.sock.outbuf = nmalloc(socklist[i].handler.sock.outbuflen - x); |
1210 |
egg_memcpy(socklist[i].handler.sock.outbuf, p + x, socklist[i].handler.sock.outbuflen - x); |
1211 |
socklist[i].handler.sock.outbuflen -= x; |
1212 |
nfree(p); |
1213 |
} else { |
1214 |
debug3("dequeue_sockets(): errno = %d (%s) on %d", errno, |
1215 |
strerror(errno), socklist[i].sock); |
1216 |
} |
1217 |
/* All queued data was sent. Call handler if one exists and the |
1218 |
* dcc entry wants it. |
1219 |
*/ |
1220 |
if (!socklist[i].handler.sock.outbuf) { |
1221 |
int idx = findanyidx(socklist[i].sock); |
1222 |
|
1223 |
if (idx > 0 && dcc[idx].type && dcc[idx].type->outdone) |
1224 |
dcc[idx].type->outdone(idx); |
1225 |
} |
1226 |
} |
1227 |
} |
1228 |
} |
1229 |
|
1230 |
|
1231 |
/* |
1232 |
* Debugging stuff |
1233 |
*/ |
1234 |
|
1235 |
void tell_netdebug(int idx) |
1236 |
{ |
1237 |
int i; |
1238 |
char s[80]; |
1239 |
|
1240 |
dprintf(idx, "Open sockets:"); |
1241 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
1242 |
if (!(socklist[i].flags & SOCK_UNUSED)) { |
1243 |
sprintf(s, " %d", socklist[i].sock); |
1244 |
if (socklist[i].flags & SOCK_BINARY) |
1245 |
strcat(s, " (binary)"); |
1246 |
if (socklist[i].flags & SOCK_LISTEN) |
1247 |
strcat(s, " (listen)"); |
1248 |
if (socklist[i].flags & SOCK_PASS) |
1249 |
strcat(s, " (passed on)"); |
1250 |
if (socklist[i].flags & SOCK_CONNECT) |
1251 |
strcat(s, " (connecting)"); |
1252 |
if (socklist[i].flags & SOCK_STRONGCONN) |
1253 |
strcat(s, " (strong)"); |
1254 |
if (socklist[i].flags & SOCK_NONSOCK) |
1255 |
strcat(s, " (file)"); |
1256 |
#ifdef TLS |
1257 |
if (socklist[i].ssl) |
1258 |
strcat(s, " (TLS)"); |
1259 |
#endif |
1260 |
if (socklist[i].flags & SOCK_TCL) |
1261 |
strcat(s, " (tcl)"); |
1262 |
if (!(socklist[i].flags & SOCK_TCL)) { |
1263 |
if (socklist[i].handler.sock.inbuf != NULL) |
1264 |
sprintf(&s[strlen(s)], " (inbuf: %04X)", |
1265 |
(unsigned int) strlen(socklist[i].handler.sock.inbuf)); |
1266 |
if (socklist[i].handler.sock.outbuf != NULL) |
1267 |
sprintf(&s[strlen(s)], " (outbuf: %06lX)", socklist[i].handler.sock.outbuflen); |
1268 |
} |
1269 |
strcat(s, ","); |
1270 |
dprintf(idx, "%s", s); |
1271 |
} |
1272 |
} |
1273 |
dprintf(idx, " done.\n"); |
1274 |
} |
1275 |
|
1276 |
/* Security-flavoured sanity checking on DCC connections of all sorts can be |
1277 |
* done with this routine. Feed it the proper information from your DCC |
1278 |
* before you attempt the connection, and this will make an attempt at |
1279 |
* figuring out if the connection is really that person, or someone screwing |
1280 |
* around. It's not foolproof, but anything that fails this check probably |
1281 |
* isn't going to work anyway due to masquerading firewalls, NAT routers, |
1282 |
* or bugs in mIRC. |
1283 |
*/ |
1284 |
int sanitycheck_dcc(char *nick, char *from, char *ipaddy, char *port) |
1285 |
{ |
1286 |
/* According to the latest RFC, the clients SHOULD be able to handle |
1287 |
* DNS names that are up to 255 characters long. This is not broken. |
1288 |
*/ |
1289 |
|
1290 |
#ifdef IPV6 |
1291 |
char badaddress[INET6_ADDRSTRLEN]; |
1292 |
sockname_t name; |
1293 |
IP ip = 0; |
1294 |
#else |
1295 |
char badaddress[sizeof "255.255.255.255"]; |
1296 |
IP ip = my_atoul(ipaddy); |
1297 |
#endif |
1298 |
int prt = atoi(port); |
1299 |
|
1300 |
/* It is disabled HERE so we only have to check in *one* spot! */ |
1301 |
if (!dcc_sanitycheck) |
1302 |
return 1; |
1303 |
|
1304 |
if (prt < 1) { |
1305 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible port of %u!", |
1306 |
nick, from, prt); |
1307 |
return 0; |
1308 |
} |
1309 |
#ifdef IPV6 |
1310 |
if (strchr(ipaddy, ':')) { |
1311 |
if (inet_pton(AF_INET6, ipaddy, &name.addr.s6.sin6_addr) != 1) { |
1312 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an invalid IPv6 " |
1313 |
"address of %s!", nick, from, ipaddy); |
1314 |
return 0; |
1315 |
} |
1316 |
if (IN6_IS_ADDR_V4MAPPED(&name.addr.s6.sin6_addr)) |
1317 |
ip = ntohl(*(IP *) &name.addr.s6.sin6_addr.s6_addr[12]); |
1318 |
} |
1319 |
#endif |
1320 |
if (ip && inet_ntop(AF_INET, &ip, badaddress, sizeof badaddress) && |
1321 |
(ip < (1 << 24))) { |
1322 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) specified an impossible IP of %s!", |
1323 |
nick, from, badaddress); |
1324 |
return 0; |
1325 |
} |
1326 |
return 1; |
1327 |
} |
1328 |
|
1329 |
int hostsanitycheck_dcc(char *nick, char *from, sockname_t *ip, char *dnsname, |
1330 |
char *prt) |
1331 |
{ |
1332 |
char badaddress[INET6_ADDRSTRLEN]; |
1333 |
|
1334 |
/* According to the latest RFC, the clients SHOULD be able to handle |
1335 |
* DNS names that are up to 255 characters long. This is not broken. |
1336 |
*/ |
1337 |
char hostn[256]; |
1338 |
|
1339 |
/* It is disabled HERE so we only have to check in *one* spot! */ |
1340 |
if (!dcc_sanitycheck) |
1341 |
return 1; |
1342 |
strcpy(badaddress, iptostr(&ip->addr.sa)); |
1343 |
/* These should pad like crazy with zeros, since 120 bytes or so is |
1344 |
* where the routines providing our data currently lose interest. I'm |
1345 |
* using the n-variant in case someone changes that... |
1346 |
*/ |
1347 |
strncpyz(hostn, extracthostname(from), sizeof hostn); |
1348 |
if (!egg_strcasecmp(hostn, dnsname)) { |
1349 |
putlog(LOG_DEBUG, "*", "DNS information for submitted IP checks out."); |
1350 |
return 1; |
1351 |
} |
1352 |
if (!strcmp(badaddress, dnsname)) |
1353 |
putlog(LOG_MISC, "*", "ALERT: (%s!%s) sent a DCC request with bogus IP " |
1354 |
"information of %s port %s. %s does not resolve to %s!", nick, from, |
1355 |
badaddress, prt, from, badaddress); |
1356 |
else |
1357 |
return 1; /* <- usually happens when we have |
1358 |
* a user with an unresolved hostmask! */ |
1359 |
return 0; |
1360 |
} |
1361 |
|
1362 |
/* Checks wether the referenced socket has data queued. |
1363 |
* |
1364 |
* Returns true if the incoming/outgoing (depending on 'type') queues |
1365 |
* contain data, otherwise false. |
1366 |
*/ |
1367 |
int sock_has_data(int type, int sock) |
1368 |
{ |
1369 |
int ret = 0, i; |
1370 |
|
1371 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) |
1372 |
if (!(socklist[i].flags & SOCK_UNUSED) && socklist[i].sock == sock) |
1373 |
break; |
1374 |
if (i < threaddata()->MAXSOCKS) { |
1375 |
switch (type) { |
1376 |
case SOCK_DATA_OUTGOING: |
1377 |
ret = (socklist[i].handler.sock.outbuf != NULL); |
1378 |
break; |
1379 |
case SOCK_DATA_INCOMING: |
1380 |
ret = (socklist[i].handler.sock.inbuf != NULL); |
1381 |
break; |
1382 |
} |
1383 |
} else |
1384 |
debug1("sock_has_data: could not find socket #%d, returning false.", sock); |
1385 |
return ret; |
1386 |
} |
1387 |
|
1388 |
/* flush_inbuf(): |
1389 |
* checks if there's data in the incoming buffer of an connection |
1390 |
* and flushs the buffer if possible |
1391 |
* |
1392 |
* returns: -1 if the dcc entry wasn't found |
1393 |
* -2 if dcc[idx].type->activity doesn't exist and the data couldn't |
1394 |
* be handled |
1395 |
* 0 if buffer was empty |
1396 |
* otherwise length of flushed buffer |
1397 |
*/ |
1398 |
int flush_inbuf(int idx) |
1399 |
{ |
1400 |
int i, len; |
1401 |
char *inbuf; |
1402 |
|
1403 |
Assert((idx >= 0) && (idx < dcc_total)); |
1404 |
for (i = 0; i < threaddata()->MAXSOCKS; i++) { |
1405 |
if ((dcc[idx].sock == socklist[i].sock) && |
1406 |
!(socklist[i].flags & SOCK_UNUSED)) { |
1407 |
len = socklist[i].handler.sock.inbuflen; |
1408 |
if ((len > 0) && socklist[i].handler.sock.inbuf) { |
1409 |
if (dcc[idx].type && dcc[idx].type->activity) { |
1410 |
inbuf = socklist[i].handler.sock.inbuf; |
1411 |
socklist[i].handler.sock.inbuf = NULL; |
1412 |
dcc[idx].type->activity(idx, inbuf, len); |
1413 |
nfree(inbuf); |
1414 |
return len; |
1415 |
} else |
1416 |
return -2; |
1417 |
} else |
1418 |
return 0; |
1419 |
} |
1420 |
} |
1421 |
return -1; |
1422 |
} |
1423 |
|
1424 |
/* Find sock in socklist. |
1425 |
* |
1426 |
* Returns index in socklist or -1 if not found. |
1427 |
*/ |
1428 |
int findsock(int sock) |
1429 |
{ |
1430 |
int i; |
1431 |
struct threaddata *td = threaddata(); |
1432 |
|
1433 |
for (i = 0; i < td->MAXSOCKS; i++) |
1434 |
if (td->socklist[i].sock == sock) |
1435 |
break; |
1436 |
if (i == td->MAXSOCKS) |
1437 |
return -1; |
1438 |
return i; |
1439 |
} |