1 /*
2 * server.c: interactions with servers, tcp code, and DNS resolver
3 *
4 * Copyright(c) 1997-2000 - All Rights Reserved
5 *
6 * See the COPYRIGHT file.
7 */
8
9 #ifndef lint
10 static char rcsid[] = "@(#)$Id: server.c,v 1.83 2001/08/01 01:21:42 kalt Exp $";
11 #endif
12
13 #include "os.h"
14
15 #if HAVE_SYS_FILIO_H
16 # include <sys/filio.h>
17 #endif
18
19 #include "struct.h"
20 #include "term.h"
21 #include "window.h"
22 #include "option.h"
23 #include "config.h"
24 #include "utils.h"
25 #include "server.h"
26
27 extern char need_collect;
28 extern struct window_ *current;
29
30 extern void sic_format(char *, char *, char *, char *);
31
32 static struct server_ *slist = NULL;
33 struct server_ *server = NULL;
34
35 static int
sic_nextaddr(srv)36 sic_nextaddr(srv)
37 struct server_ *srv;
38 {
39 #if defined(HAVE_GETADDRINFO)
40 struct addrinfo *last;
41
42 assert(srv->address);
43 assert(srv->ip);
44
45 last = srv->addrip;
46 do
47 {
48 srv->addrip = srv->addrip->ai_next;
49 if (srv->addrip == NULL)
50 srv->addrip = srv->address;
51 }
52 while (srv->addrip->ai_family != PF_INET &&
53 srv->addrip->ai_family != PF_INET6);
54 free(srv->ip);
55 srv->ip = aitoip(srv->addrip);
56
57 vsic_slog(LOG_DEBUG, "sic_nextaddr: srv->addrip %x, last %x",
58 srv->addrip, last);
59 if (srv->addrip != last)
60 return 1;
61 #endif
62 vsic_slog(LOG_DEBUG, "sic_nextaddr returns 0");
63 return 0;
64 }
65
66 /* sic_connect: connect() to a server (given a struct server_ pointer) */
67 void
sic_connect(srv)68 sic_connect(srv)
69 struct server_ *srv;
70 {
71 struct sockaddr_in sock_sin;
72 int fd, opt = 1, rc;
73 char *vif;
74
75 assert (srv->ip); /* even if srv->addrip is defined */
76
77 bzero((char *)&sock_sin, sizeof(sock_sin));
78 sock_sin.sin_family = AF_INET;
79
80 #if defined(HAVE_GETADDRINFO)
81 if (srv->addrip)
82 fd = socket(srv->addrip->ai_family, SOCK_STREAM,
83 srv->addrip->ai_protocol);
84 else
85 #endif
86 fd = socket(AF_INET, SOCK_STREAM, 0);
87
88 if (fd < 0)
89 vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
90 if (fd > 0 && (vif = getenv("SICIP")))
91 {
92 /* the following should be fixed rather than simply reporting an error */
93 sock_sin.sin_addr.s_addr = inet_addr(vif);
94 if (sock_sin.sin_addr.s_addr == INADDR_NONE)
95 vsic_slog(LOG_CLIENT, "--- Bad IP address: SICIP = %s", vif);
96 else
97 if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
98 vsic_slog(LOG_CLIENT, "--- Unable to bind to IP %s: %s", vif,
99 strerror(errno));
100 }
101 if (fd > 0 && ioctl(fd, FIONBIO, &opt) < 0)
102 vsic_slog(LOG_CLIENT, "--- ioctl() for %s:%d failed: %s", srv->sname,
103 srv->port, strerror(errno));
104
105 #if defined(HAVE_GETADDRINFO)
106 if (srv->addrip)
107 rc = connect(fd, srv->addrip->ai_addr, srv->addrip->ai_addrlen);
108 else
109 #endif
110 {
111 sock_sin.sin_port = htons(srv->port);
112 sock_sin.sin_addr.s_addr = inet_addr(srv->ip);
113 rc = connect(fd, (struct sockaddr *)&sock_sin, sizeof(sock_sin));
114 }
115
116 if (fd > 0 && rc < 0 && errno != EINPROGRESS)
117 {
118 rc = errno;
119 vsic_slog(LOG_CLIENT, "--- Connection to %s:%d failed: %s", srv->sname,
120 srv->port, strerror(errno));
121 close(fd);
122 if (rc == EHOSTUNREACH && !option(srv->sopt, S_DCC))
123 fd = -2;
124 else
125 fd = -1;
126 }
127 unset_option(srv->sopt, S_RECONNECT);
128 unset_option(srv->sopt, S_QUIT);
129 switch (fd)
130 {
131 case -2: /* no route to host, may be there's another address to try? */
132 unset_option(srv->sopt, S_CONNECTING);
133 if (sic_nextaddr(srv))
134 {
135 set_option(srv->sopt, S_RECONNECT);
136 srv->ts = time(NULL) - 4;
137 break;
138 }
139 srv->ts = time(NULL);
140 break;
141 case -1: /* other type of failures */
142 unset_option(srv->sopt, S_CONNECTING);
143 if (!option(srv->sopt, S_DCC)) /* needed? */
144 srv->ts = time(NULL);
145 break;
146 default: /* connection in progress */
147 set_option(srv->sopt, S_CONNECTING);
148
149 if (!option(srv->sopt, S_CONFIG))
150 cfg_read(srv->sname);
151 set_option(srv->sopt, S_CONFIG);
152 break;
153 }
154 srv->fd = fd;
155 srv->lastr = 0;
156 srv->readbuf[0] = '\0';
157 }
158
159 /* sic_listen: create a new socket, and bind it */
160 static void
sic_listen(dcc)161 sic_listen(dcc)
162 struct server_ *dcc;
163 {
164 struct sockaddr_in sock_sin;
165 int fd, sz = sizeof(struct sockaddr_in);
166 char *vif;
167
168 if (getsockname(server->fd, (struct sockaddr *)&sock_sin, &sz) < 0)
169 {
170 vsic_slog(LOG_CLIENT, "--- Unable to get my own IP: %s",strerror(errno));
171 dcc->fd = -1;
172 return;
173 }
174 else
175 sz = sizeof(struct sockaddr_in);
176
177 dcc->ip = strdup((char *)inet_ntoa(sock_sin.sin_addr));
178
179 bzero((char *)&sock_sin, sizeof(sock_sin));
180 sock_sin.sin_family = AF_INET;
181
182 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
183 vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
184 if (vif = getenv("SICIP"))
185 sock_sin.sin_addr.s_addr = inet_addr(vif);
186 else
187 sock_sin.sin_addr.s_addr = INADDR_ANY;
188 if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
189 {
190 vsic_slog(LOG_CLIENT, "--- bind() call failed: %s", strerror(errno));
191 close(fd);
192 fd = -1;
193 }
194 if (fd > 0 && listen(fd, 1) < 0)
195 {
196 vsic_slog(LOG_CLIENT, "--- listen() call failed: %s", strerror(errno));
197 close(fd);
198 fd = -1;
199 }
200 if (fd > 0)
201 if (getsockname(fd, (struct sockaddr *)&sock_sin, &sz) < 0)
202 {
203 vsic_slog(LOG_CLIENT, "--- getsockname() call failed: %s",
204 strerror(errno));
205 free(dcc->ip);
206 close(fd);
207 }
208 else
209 {
210 dcc->port = sock_sin.sin_port;
211 dcc->sname = strdup(server->sname);
212 set_option(dcc->sopt, S_LISTEN);
213 }
214 dcc->fd = fd;
215 }
216
217 /* select_server: set a different server */
218 struct server_ *
select_server(name)219 select_server(name)
220 char *name;
221 {
222 struct server_ *stmp = slist;
223
224 assert(name);
225
226 while (stmp && (strcasecmp(stmp->sname, name)
227 || !option(stmp->sopt, S_CONNECTED)))
228 stmp = stmp->nexts;
229
230 return server = stmp;
231 }
232
233 /* sic_server: adds a server to the list, does NOT connect */
234 struct server_ *
sic_server(nick,name,port,pass)235 sic_server(nick, name, port, pass)
236 char *nick, *name, *pass;
237 int port;
238 {
239 struct server_ **stmp = &slist;
240
241 while (*stmp)
242 stmp = &((*stmp)->nexts);
243
244 server = (struct server_ *) malloc(sizeof(struct server_));
245 bzero(server, sizeof(struct server_));
246
247 *stmp = server;
248
249 server->nick = strdup(nick);
250 server->sname = strdup(name);
251 server->port = port;
252 if (pass)
253 server->pass = strdup(pass);
254
255 #if defined(HAVE_GETADDRINFO)
256 {
257 struct addrinfo *address, hints;
258 char port_str[10];
259
260 bzero((char *)&hints, sizeof(hints));
261 hints.ai_flags = AI_CANONNAME|AI_NUMERICHOST;
262 hints.ai_family = PF_UNSPEC;
263 hints.ai_socktype = SOCK_STREAM;
264 sprintf(port_str, "%d", port);
265 if (getaddrinfo(name, port_str, &hints, &address) == 0)
266 {
267 server->ip = strdup(name);
268 server->address = address;
269 server->addrip = address;
270 }
271 }
272 #else
273 if (inet_addr(name) != INADDR_NONE)
274 server->ip = strdup(name);
275 #endif
276
277 if (server->ip == NULL)
278 {
279 dns_lookup(name, server);
280 set_option(server->sopt, S_DNS);
281 }
282
283 set_option(server->sopt, S_NOTCONNECTED);
284 set_option(server->sopt, S_QUIT);
285 server->fd = -1;
286 return server;
287 }
288
289 /* sic_dserver: removes a server from the list */
290 void
sic_dserver(del)291 sic_dserver(del)
292 struct server_ *del;
293 {
294 struct server_ **stmp = &slist;
295
296 assert(sic_scnt(del) == 0);
297 assert(option(del->sopt, S_CONNECTED) == 0);
298 assert(option(del->sopt, S_DNS) == 0);
299 while (*stmp != del)
300 stmp = &((*stmp)->nexts);
301 *stmp = del->nexts;
302 opt_free(&(del->custs));
303 free(del->sname);
304 if (del->ip)
305 free(del->ip);
306 #if defined(HAVE_GETADDRINFO)
307 if (del->address)
308 freeaddrinfo(del->address);
309 #endif
310 if (del->pass)
311 free(del->pass);
312 free(del->nick);
313 if (del->uname)
314 free(del->uname);
315 if (del->rname)
316 free(del->rname);
317 if (del->readf)
318 close(del->readf);
319 if (del->sendf)
320 close(del->sendf);
321 free(del);
322 }
323
324 static void
sic_write(str)325 sic_write(str)
326 char *str;
327 {
328 if (server && server->fd >= 0)
329 {
330 if (write(server->fd, str, strlen(str)) == -1
331 && (errno == EAGAIN || errno == EWOULDBLOCK))
332 /* should be dealt with in a better way, not worth my time unless
333 it happens */
334 vsic_slog(LOG_CLIENT, "--- write() to server failed: %s",
335 strerror(errno));
336 str[strlen(str)-1] = '\0'; /* hmmpf */
337 sic_slog(LOG_OSNIF, str);
338 }
339 else
340 sic_slog(LOG_CLIENT, "--- No server");
341 }
342
343 void
vsic_write(char * format,...)344 vsic_write(char *format, ...)
345 {
346 char buffer[1024];
347 va_list va;
348
349 va_start(va, format);
350 vsprintf(buffer, format, va);
351 va_end(va);
352 strcat(buffer, "\n");
353 sic_write(buffer);
354 }
355
356 static int
readln(fd,buffer,length)357 readln(fd, buffer, length)
358 int fd, length;
359 char *buffer;
360 {
361 int status, ptr;
362
363 ptr = -1;
364 while (buffer[++ptr]);
365
366 while (((status = read(fd, &buffer[ptr], 1)) > 0) && (ptr < length - 1))
367 {
368 if (buffer[ptr++] == '\n')
369 break;
370 }
371
372 if (status == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
373 {
374 /* incomplete line */
375 buffer[ptr] = '\0';
376 return -2;
377 }
378
379 if (status > 0)
380 {
381 if (buffer[ptr-2] == '\r')
382 buffer[ptr-2] = '\0';
383 else
384 buffer[ptr-1] = '\0';
385 sic_slog(LOG_ISNIF, buffer);
386 }
387
388 return (status);
389 }
390
391 /* this function is totally buggy, but it's simple and works most of the time*/
392 static void
parse(str)393 parse(str)
394 char *str;
395 {
396 char *sender, *cmd, *dest, *para;
397
398 if (option(server->sopt, S_DCC))
399 {
400 char snder[267];
401
402 sprintf(snder, "%s@%s", server->nick, server->sname);
403 sic_format(snder, NULL, NULL, str);
404 return;
405 }
406
407 if (*str == ':')
408 {
409 sender = ++str;
410 while (*++str != ' ');
411 *str++ = '\0';
412 }
413 else
414 sender = server->sname;
415
416 if (!strncmp(str, "ERROR ", 6))
417 {
418 cmd = "ERROR";
419 dest = server->nick;
420 para = str;
421 }
422 else
423 {
424 cmd = str;
425 while (*++str != ' ');
426 *str++ = '\0';
427 if (*str == ':')
428 {
429 vsic_slog(LOG_DEBUG, "dest begins with : for `%s %s %s'",sender,cmd,str);
430 dest = str+1;
431 para = "";
432 }
433 else
434 {
435 dest = str;
436 while (*++str != ' ' && *str);
437 if (*str)
438 {
439 *str++ = '\0';
440 if (*str == ':')
441 para = str + 1;
442 else
443 para = str;
444 }
445 else
446 para = "";
447 }
448 }
449
450 if (strcmp(cmd, "PONG"))
451 sic_format(sender, cmd, dest, para);
452 }
453
454 /* sic_select: big fat io loop */
455 int
sic_select()456 sic_select()
457 {
458 fd_set rfd, wfd, efd;
459 int n, maxfd = 0;
460 struct server_ *stmp = slist;
461 struct timeval timeout;
462 char buf[10240];
463
464 FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
465 timeout.tv_usec = 0; timeout.tv_sec = 5;
466
467 FD_SET(0, &rfd);
468 while (stmp)
469 {
470 if (stmp->fd > maxfd)
471 maxfd = stmp->fd;
472 server = stmp;
473 if (option(stmp->sopt, S_NOTCONNECTED)
474 && !option(stmp->sopt, S_CONNECTING)
475 && !option(stmp->sopt, S_CONNECTED)
476 && !option(stmp->sopt, S_QUIT)
477 && !option(stmp->sopt, S_DCC)
478 && (time(NULL) - stmp->ts) >= 1800
479 && get_option(Z_RECONNECT, NULL))
480 {
481 select_active(NULL, 0);
482 vsic_slog(LOG_CLIENT, "--- Attempting to reconnect to %s:%d",
483 stmp->sname, stmp->port);
484 set_option(stmp->sopt, S_RECONNECT);
485 }
486 if (option(stmp->sopt, S_RECONNECT) && (time(NULL) - stmp->ts) >= 5)
487 {
488 select_active(NULL, 0);
489 sic_connect(stmp);
490 }
491 if (option(stmp->sopt, S_CONNECTED))
492 {
493 if (!option(stmp->sopt, S_DCC))
494 {
495 server = stmp;
496 select_active(NULL, 0);
497 if (stmp->lastr && time(NULL) - stmp->lastr > 60)
498 {
499 if ((time(NULL) - stmp->lastr) % 120 < 60)
500 {
501 if (!option(stmp->sopt, S_PING))
502 {
503 vsic_write("PING .");
504 set_option(stmp->sopt, S_PING);
505 }
506 }
507 else
508 unset_option(stmp->sopt, S_PING);
509 }
510 if (need_collect)
511 collect_channel();
512 }
513 FD_SET(stmp->fd, &rfd);
514 }
515 if (option(stmp->sopt, S_SEND) && stmp->sent < stmp->size)
516 FD_SET(stmp->fd, &wfd);
517 if (option(stmp->sopt, S_LISTEN))
518 FD_SET(stmp->fd, &rfd);
519 if (option(stmp->sopt, S_CONNECTING) && !option(stmp->sopt, S_DNS))
520 FD_SET(stmp->fd, &wfd);
521 stmp = stmp->nexts;
522 }
523
524 display_status();
525 term_input(NULL, 0);
526 term_flush();
527 #if defined(GNU_PTH)
528 n = pth_select(maxfd + 1, &rfd, &wfd, NULL, &timeout);
529 #else
530 n = select(maxfd + 1, &rfd, &wfd, &efd, &timeout);
531 #endif
532
533 if (n < 0)
534 return 0;
535
536 stmp = slist;
537 while (stmp)
538 {
539 /* first deal with pending DCCs */
540 if (stmp->fd >= 0 && option(stmp->sopt, S_LISTEN))
541 {
542 if (FD_ISSET(stmp->fd, &rfd))
543 {
544 struct sockaddr_in sock_sin;
545 int newfd, sz = sizeof(struct sockaddr_in);
546
547 server = stmp;
548 unset_option(stmp->sopt, S_LISTEN);
549 set_option(stmp->sopt, S_DCC);
550 select_active(NULL, 0);
551 if ((newfd=accept(stmp->fd,(struct sockaddr *)&sock_sin,&sz))<0)
552 {
553 vsic_slog(LOG_CLIENT, "DCC accept() failed for %s@%s: %s",
554 stmp->nick, inet_ntoa(sock_sin.sin_addr),
555 strerror(errno));
556 stmp = stmp->nexts;
557 sic_dserver(server);
558 }
559 else
560 {
561 close(stmp->fd);
562 stmp->fd = newfd;
563 free(stmp->sname);
564 stmp->sname = strdup((char *)inet_ntoa(sock_sin.sin_addr));
565 set_option(stmp->sopt, S_CONNECTED);
566 vsic_slog(LOG_CLIENT,
567 "--- DCC connection received from %s@%s",
568 stmp->nick, stmp->sname);
569 dns_lookup(stmp->sname, NULL);
570 sz = 1;
571 if (ioctl(stmp->fd, FIONBIO, &sz) < 0)
572 vsic_slog(LOG_CLIENT, "--- ioctl() failed: %s",
573 strerror(errno));
574 if (stmp->pass)
575 set_option(stmp->sopt, S_SEND);
576 stmp->ts = time(NULL);
577 }
578 }
579 }
580 /* DCC sends */
581 if (option(stmp->sopt, S_SEND))
582 {
583 server = stmp;
584 select_active(NULL, 0);
585 if (FD_ISSET(stmp->fd, &rfd))
586 {
587 u_32int_t total; /* DCC ack */
588
589 if (recv(stmp->fd, (char *) &total, sizeof(u_32int_t),0) > 0)
590 stmp->read = ntohl(total);
591 else
592 {
593 vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost: %s",
594 stmp->pass, stmp->nick, strerror(errno));
595 vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
596 stmp->read, stmp->size,
597 (int)(100*stmp->read/stmp->size));
598 close(stmp->fd);
599 stmp = stmp->nexts;
600 sic_dserver(server);
601 continue;
602 }
603 if (stmp->read == stmp->size)
604 {
605 vsic_slog(LOG_CLIENT,
606 "--- DCC \"%s\" to %s completed in %s (%ub/s)",
607 stmp->pass, stmp->nick,
608 sic_tdiff(time(NULL)-stmp->ts, 0),
609 stmp->size/(1+time(NULL)-stmp->ts));
610 shutdown(stmp->fd, 2);
611 close(stmp->fd);
612 stmp = stmp->nexts;
613 sic_dserver(server);
614 continue;
615 }
616 }
617 if (FD_ISSET(stmp->fd, &wfd) && stmp->sent - stmp->read < 10240)
618 {
619 char error = 0;
620 unsigned long rd, st = 0;
621
622 if (lseek(stmp->sendf, stmp->sent, SEEK_SET) < 0)
623 error = 1;
624 if ((rd = read(stmp->sendf, buf, 10240)) < 0)
625 error = 2;
626 if (rd > 0 && ((st = write(stmp->fd, buf, rd)) < 0))
627 error = 3;
628 if (error)
629 {
630 if (error != 3)
631 vsic_slog(LOG_CLIENT, "--- %s() failed: %s!",
632 (error == 1) ?"lseek":"read",strerror(errno));
633 vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost.",
634 stmp->pass, stmp->nick);
635 vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
636 stmp->read, stmp->size,
637 (int)(100*stmp->read/stmp->size));
638 close(stmp->fd);
639 stmp = stmp->nexts;
640 sic_dserver(server);
641 continue;
642 }
643 stmp->sent += st;
644 }
645 stmp = stmp->nexts;
646 continue;
647 }
648 /* DCC chat, DCC get, and normal server traffic */
649 if (stmp->fd >= 0 && !option(stmp->sopt, S_LISTEN))
650 {
651 server = stmp;
652 select_active(NULL, 0); /* default window for this server */
653 if (FD_ISSET(stmp->fd, &rfd))
654 {
655 int sz;
656
657 if (option(stmp->sopt, S_DCC) && stmp->pass)
658 sz = read(stmp->fd, buf, 8192); /* big buffer for files */
659 else
660 {
661 /*read up to \r\n otherwise*/
662 unset_option(stmp->sopt, S_PING);
663 sz = readln(stmp->fd, stmp->readbuf, 520);
664 }
665 if (sz > 0)
666 {
667 stmp->lastr = time(NULL);
668 if (option(stmp->sopt, S_NOTCONNECTED))
669 {
670 assert(!option(stmp->sopt, S_DCC));
671 unset_option(stmp->sopt, S_NOTCONNECTED);
672 sic_slog(LOG_CLIENT, "--- Connection established.");
673 }
674 if (option(stmp->sopt, S_DCC) && stmp->pass)
675 { /* file transfer */
676 if (stmp->readf == 0)
677 stmp->readf = open(stmp->pass,
678 O_WRONLY|O_TRUNC|O_CREAT, 0644);
679 if (stmp->readf == 0)
680 {
681 vsic_slog(LOG_CLIENT,
682 "--- Unable to create file \"%s\"!",
683 stmp->pass);
684 close(stmp->fd);
685 stmp = stmp->nexts;
686 sic_dserver(server); /* "continue;" below */
687 }
688 else
689 {
690 u_32int_t total; /* DCC ack */
691
692 write(stmp->readf, buf, sz); /* ugly! */
693 total = htonl(stmp->read += sz);
694 send(stmp->fd, (char *) &total, sizeof(u_32int_t),0);
695 if (stmp->read == stmp->size) /* got all of it? */
696 {
697 stmp->size /= 1024;
698 vsic_slog(LOG_CLIENT,
699 "--- DCC \"%s\" from %s completed in %s (%ub/s)",
700 stmp->pass, stmp->nick,
701 sic_tdiff(time(NULL)-stmp->ts, 0),
702 stmp->size/(1+time(NULL)-stmp->ts));
703 shutdown(stmp->fd, 2);
704 close(stmp->fd);
705 stmp = stmp->nexts;
706 sic_dserver(server); /* "continue;" below */
707 }
708 else
709 stmp = stmp->nexts;
710 }
711 continue;
712 }
713 else
714 {
715 /* readln() */
716 if (!option(stmp->sopt, S_DCC)
717 && !strncmp("PING ", stmp->readbuf, 5))
718 vsic_write("PONG %s :%s", server->nick,
719 (stmp->readbuf[4] && stmp->readbuf[5]) ?
720 stmp->readbuf+6 : "");
721 else
722 {
723 /* current window for this server */
724 select_active(NULL, 1);
725 parse(stmp->readbuf);
726 }
727 stmp->readbuf[0] = '\0';
728 }
729 }
730 else if (sz == -2)
731 {
732 vsic_slog(LOG_DEBUG, "Incomplete line from %s", stmp->sname);
733 }
734 else
735 {
736 close(stmp->fd);
737 stmp->fd = -1;
738 if (option(stmp->sopt, S_NOTCONNECTED))
739 {
740 /* this can happen when connecting locally */
741 assert(!option(stmp->sopt, S_DCC));
742 vsic_slog(LOG_CLIENT,
743 "--- Connection to %s %d [%s] failed.",
744 stmp->sname, stmp->port, stmp->ip);
745 }
746 set_option(stmp->sopt, S_NOTCONNECTED);
747 unset_option(stmp->sopt, S_CONNECTED);
748 if ((time(NULL) - stmp->ts) > 60
749 && !option(stmp->sopt, S_DCC)
750 && !option(stmp->sopt, S_QUIT)
751 && get_option(Z_RECONNECT, NULL))
752 set_option(stmp->sopt, S_RECONNECT);
753 stmp->ts = time(NULL);
754 if (option(stmp->sopt, S_DCC))
755 {
756 vsic_slog(LOG_CLIENT, "--- DCC to %s@%s lost.",
757 stmp->nick, stmp->sname);
758 if (stmp->pass)
759 {
760 vsic_slog(LOG_CLIENT,
761 "--- Got %lu out of %lu (%u%% of %s).",
762 stmp->read, stmp->size,
763 (int)(100*stmp->read/stmp->size),
764 stmp->pass);
765 stmp = stmp->nexts;
766 sic_dserver(server);
767 }
768 }
769 else
770 vsic_slog(LOG_CLIENT,
771 "--- Lost connection to %s:%d : %s",
772 stmp->sname, stmp->port,
773 option(stmp->sopt, S_RECONNECT) ?
774 "Reconnecting..." :
775 option(stmp->sopt, S_QUIT) || !get_option(Z_RECONNECT, NULL) ?
776 "No reconnect.":"Will attempt to reconnect.");
777 continue;
778 }
779 }
780 /* connection establishment */
781 if (FD_ISSET(stmp->fd, &wfd))
782 {
783 char *wp, buffer[512];
784
785 unset_option(stmp->sopt, S_CONNECTING);
786
787 if (!option(stmp->sopt, S_DCC))
788 {
789 server->umode[0] = '\0';
790 sprintf(buffer, "%s%s%sNICK %s\nUSER %s myhost 0 :%s\n",
791 (server->pass) ? "PASS " : "",
792 (server->pass) ? server->pass : "",
793 (server->pass) ? "\n" : "",
794 stmp->nick,
795 (stmp->uname) ? stmp->uname :
796 (wp = getenv("SICUSER")) ? wp :
797 (wp = getenv("USER")) ? wp : "sicuser",
798 (stmp->rname) ? stmp->rname :
799 (wp = getenv("SICNAME")) ? wp :"wasting time");
800 if (write(stmp->fd, buffer, strlen(buffer)) == -1)
801 {
802 close(stmp->fd);
803 stmp->fd = -1;
804 stmp->ts = time(NULL);
805 set_option(stmp->sopt, S_NOTCONNECTED);
806 sic_slog(LOG_CLIENT, "--- Connection failed.");
807 }
808 else
809 {
810 del_member(NULL, NULL);
811 set_option(stmp->sopt, S_CONNECTED);
812 }
813 }
814 else
815 {
816 vsic_slog(LOG_CLIENT, "--- DCC to %s@%s established.",
817 stmp->nick, stmp->sname);
818 set_option(stmp->sopt, S_CONNECTED);
819 unset_option(stmp->sopt, S_NOTCONNECTED);
820 }
821 }
822 }
823 stmp = stmp->nexts;
824 }
825
826 if (FD_ISSET(0, &rfd))
827 return 1;
828 else
829 return 0;
830 }
831
832 int
cmd_server(p)833 cmd_server(p)
834 char *p;
835 {
836 struct server_ *stmp = slist;
837 int n = 0;
838
839 if (p == NULL)
840 {
841 /* NULL argument, this is only used internally */
842 if (server == NULL)
843 return 0;
844 if (option(server->sopt, S_CONNECTING)
845 || option(server->sopt, S_CONNECTED))
846 {
847 if (option(server->sopt, S_QUIT))
848 {
849 if (server->fd >= 0)
850 close(server->fd);
851 server->fd = -1;
852 set_option(server->sopt, S_NOTCONNECTED);
853 unset_option(server->sopt, S_CONNECTING);
854 unset_option(server->sopt, S_CONNECTED);
855 vsic_slog(LOG_CLIENT, "--- Closed connection to %s:%d",
856 server->sname, server->port);
857 }
858 }
859 unset_option(server->sopt, S_RECONNECT);
860 set_option(server->sopt, S_QUIT);
861 return 0;
862 }
863 /* real user input */
864 if (!*p || !strcasecmp(p, "-v"))
865 {
866 /* no argument or just `-v', user is simply requesting a list */
867 sic_slog(LOG_CLIENT, "--- Server list:");
868 while (stmp)
869 {
870 if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN))
871 vsic_slog(LOG_CLIENT, "--- %2d: %s %d %s(%s%s)%s",
872 n++, stmp->sname, stmp->port,
873 (stmp->pass) ? "*password* " : "",
874 (option(stmp->sopt, S_CONNECTING)) ? "will be " :
875 (option(stmp->sopt, S_CONNECTED)) ? "" : "was ",
876 stmp->nick,
877 (stmp->ip) ? "" : option(stmp->sopt, S_DNS) ?
878 " Looking up address" : " Unknown address");
879 if (*p && stmp->ip)
880 #if defined(HAVE_GETADDRINFO)
881 {
882 struct addrinfo *address = stmp->address;
883
884 vsic_slog(LOG_CLIENT, "--- => %s[%s]",
885 (stmp->address && stmp->address->ai_canonname) ?
886 stmp->address->ai_canonname : "???", stmp->ip);
887 if (address && address->ai_next)
888 while (address)
889 {
890 if (address->ai_family == PF_INET ||
891 address->ai_family == PF_INET6)
892 vsic_slog(LOG_CLIENT, "--- > %s[%s]",
893 (address->ai_canonname) ?
894 address->ai_canonname : "???",
895 aitoip(address));
896 address = address->ai_next;
897 }
898 }
899 #else
900 vsic_slog(LOG_CLIENT, "--- => [%s]", stmp->ip);
901 #endif
902 stmp = stmp->nexts;
903 }
904 }
905 else if (!strncasecmp(p, "-d", 2))
906 {
907 char *number = p;
908
909 while (*number && !isdigit(*number))
910 number += 1;
911 if (*number == '\0')
912 return -4;
913
914 n = 0;
915 while (stmp)
916 {
917 if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
918 && n++ == atoi(number))
919 break;
920 stmp = stmp->nexts;
921 }
922 if (stmp == NULL)
923 return -4;
924
925 if (option(stmp->sopt, S_DNS))
926 {
927 sic_slog(LOG_CLIENT,
928 "--- DNS lookup in progress, cannot delete server. (try again)");
929 return 0;
930 }
931 if (option(stmp->sopt, S_CONNECTED))
932 {
933 sic_slog(LOG_CLIENT, "--- Close connection first.");
934 return 0;
935 }
936 if (n = sic_scnt(stmp))
937 {
938 vsic_slog(LOG_CLIENT,"--- %d window(s) are bound to this server.",n);
939 return 0;
940 }
941 assert(server != stmp); /* this shouldn't be possible? */
942 vsic_slog(LOG_CLIENT,"--- Deleted server %d (%s).", atoi(number),
943 stmp->sname);
944 sic_dserver(stmp);
945 return 0;
946 }
947 else
948 {
949 char *number = p;
950
951 if (*p == '+')
952 number = p+1;
953 while (*number)
954 if (!isdigit(*number++))
955 {
956 number = NULL;
957 break;
958 }
959
960 if (number)
961 {
962 /* argument is a number, find selected server and try to connect */
963 if (current->via && (option(current->via->sopt, S_CONNECTED)
964 || option(current->via->sopt, S_CONNECTING)))
965 {
966 sic_slog(LOG_CLIENT, "--- This window is already active.");
967 return 0;
968 }
969 while (stmp)
970 {
971 if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
972 && n++ == atoi(p))
973 break;
974 stmp = stmp->nexts;
975 }
976 if (stmp)
977 {
978 server = stmp;
979 if (stmp->ip || option(stmp->sopt, S_DNS))
980 {
981 if (sic_swin(1))
982 {
983 if (!option(stmp->sopt, S_CONNECTED))
984 {
985 set_option(stmp->sopt, S_CONNECTING);
986 if (option(stmp->sopt, S_DNS))
987 {
988 vsic_slog(LOG_CLIENT,
989 "--- Connection to %s pending DNS lookup.",
990 stmp->sname);
991 }
992 else
993 {
994 if (*p == '+')
995 sic_nextaddr(stmp);
996 vsic_slog(LOG_CLIENT,
997 "--- Connecting to %s %d [%s]",
998 stmp->sname, stmp->port, stmp->ip);
999 sic_connect(stmp);
1000 }
1001 }
1002 }
1003 }
1004 else
1005 vsic_slog(LOG_CLIENT, "--- Unknown IP for %s", stmp->sname);
1006 }
1007 else
1008 sic_slog(LOG_CLIENT, "--- No such server.");
1009 }
1010 else
1011 {
1012 /* format: /server name port pass */
1013 char *port, *pass = NULL;
1014 int portn = 6667;
1015
1016 if (port = index(p, ' '))
1017 {
1018 *port++ = '\0';
1019 if (pass = index(port, ' '))
1020 *pass++ = '\0';
1021 portn = atoi(port);
1022 }
1023 sic_server((current->via) ? current->via->nick : "sic",
1024 p, portn, pass);
1025 cmd_server("");
1026 }
1027 }
1028 return 0;
1029 }
1030
1031 /*
1032 * DCC code
1033 */
1034
1035 static void
sic_dwrite(nick,str)1036 sic_dwrite(nick, str)
1037 char *nick, *str;
1038 {
1039 struct server_ *dtmp = slist;
1040
1041 while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, nick)))
1042 dtmp = dtmp->nexts;
1043 if (dtmp)
1044 if (dtmp->fd >= 0)
1045 {
1046 if (write(dtmp->fd, str, strlen(str)) == -1
1047 && (errno == EAGAIN || errno == EWOULDBLOCK))
1048 /* should be dealt with in a better way, not worth my time unless
1049 it happens */
1050 vsic_slog(LOG_CLIENT, "--- write() to DCC failed: %s",
1051 strerror(errno));
1052 str[strlen(str)-1] = '\0'; /* hmmpf */
1053 sic_slog(LOG_OSNIF, str);
1054 }
1055 else
1056 sic_slog(LOG_CLIENT, "--- DCC not established.");
1057 else
1058 sic_slog(LOG_CLIENT, "--- No such DCC.");
1059 }
1060
1061 void
vsic_dwrite(char * nick,char * format,...)1062 vsic_dwrite(char *nick, char *format, ...)
1063 {
1064 char buffer[1024];
1065 va_list va;
1066
1067 va_start(va, format);
1068 vsprintf(buffer, format, va);
1069 va_end(va);
1070 strcat(buffer, "\n");
1071 sic_dwrite(nick, buffer);
1072 }
1073
1074 /* sic_dcc: called to deal with DCC requests
1075 * 0 -> chat , 1 -> send
1076 */
1077 void
sic_dcc(type,nick,ip,port,file,size)1078 sic_dcc(type, nick, ip, port, file, size)
1079 int type;
1080 char *nick, *file;
1081 unsigned long ip, size;
1082 unsigned int port;
1083 {
1084 struct server_ *dtmp = slist;
1085 unsigned long lip;
1086
1087 assert(type == 0 || type == 1);
1088
1089 while (dtmp)
1090 {
1091 if (option(dtmp->sopt, S_DCC)
1092 && !strcasecmp(dtmp->nick, nick)
1093 && (!dtmp->pass || !strcmp(dtmp->pass, file)))
1094 break;
1095 dtmp = dtmp->nexts;
1096 }
1097 if (dtmp)
1098 {
1099 /* should be dealt with more gracefully */
1100 if ((option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED))
1101 && (type == 0 || (dtmp->pass && !strcmp(dtmp->pass, file))))
1102 {
1103 vsic_slog(LOG_CLIENT,
1104 "--- DCC chat request from %s ignored (duplicate).", nick);
1105 return;
1106 }
1107 vsic_slog(LOG_CLIENT,
1108 "--- old DCC chat request from %s deleted.", nick);
1109 sic_dserver(dtmp);
1110 }
1111 if (type == 1 && size == 0)
1112 {
1113 vsic_slog(LOG_CLIENT, "--- Invalid DCC request (file size = 0) from %s.",
1114 nick);
1115 return;
1116 }
1117 dtmp = (struct server_ *) malloc(sizeof(struct server_));
1118 bzero(dtmp, sizeof(struct server_));
1119 dtmp->fd = -1;
1120 dtmp->ts = time(NULL);
1121 set_option(dtmp->sopt, S_DCC);
1122 dtmp->nick = strdup(nick);
1123 if (type == 1)
1124 {
1125 dtmp->pass = strdup(file);
1126 dtmp->size = size;
1127 }
1128 lip = ntohl(ip);
1129 dtmp->sname = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
1130 dtmp->ip = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
1131 set_option(dtmp->sopt, S_NOTCONNECTED);
1132 dns_lookup(dtmp->ip, NULL);
1133 switch (type)
1134 {
1135 case 0:
1136 vsic_slog(LOG_CLIENT, "--- DCC CHAT request from %s@%s.",
1137 dtmp->nick, dtmp->ip);
1138 break;
1139 case 1:
1140 vsic_slog(LOG_CLIENT, "--- DCC SEND request from %s@%s: %s %lu",
1141 dtmp->nick, dtmp->ip, file, size);
1142 break;
1143 default:
1144 abort(); /* never */
1145 }
1146 if ((dtmp->port = port) < 1024)
1147 vsic_slog(LOG_CLIENT, "--- DCC request from a privileged port %d!",port);
1148 dtmp->nexts = slist;
1149 slist = dtmp;
1150 }
1151
1152 int
cmd_dcc(p)1153 cmd_dcc(p)
1154 char *p;
1155 {
1156 struct server_ *dtmp = slist;
1157
1158 if (!*p)
1159 {
1160 sic_slog(LOG_CLIENT, "--- DCC list:");
1161 while (dtmp)
1162 {
1163 if (option(dtmp->sopt, S_DCC))
1164 if (dtmp->pass)
1165 vsic_slog(LOG_CLIENT,
1166 "--- %s@%s:%d %s [%lu] %s %s: %d%% %s",
1167 dtmp->nick, dtmp->sname, dtmp->port, dtmp->pass,
1168 dtmp->size,
1169 (option(dtmp->sopt, S_CONNECTED)) ? "active" :
1170 (option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
1171 "not connected",
1172 (option(dtmp->sopt,S_SEND)) ? "Sending":"Reading",
1173 (int)(100*dtmp->read/dtmp->size),
1174 sic_tdiff(time(NULL)-dtmp->ts, 0));
1175 else
1176 vsic_slog(LOG_CLIENT, "--- %s@%s:%d %s",
1177 dtmp->nick, dtmp->sname, dtmp->port,
1178 (option(dtmp->sopt, S_CONNECTED)) ? "active" :
1179 (option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
1180 "not connected");
1181 if (option(dtmp->sopt, S_LISTEN))
1182 vsic_slog(LOG_CLIENT, "--- %s@%s pending %s%s", dtmp->nick,
1183 dtmp->sname, (dtmp->pass) ? "Send: " : "chat",
1184 (dtmp->pass) ? dtmp->pass : "");
1185 dtmp = dtmp->nexts;
1186 }
1187 return 0;
1188 }
1189 if (server == NULL)
1190 return -5;
1191 if (!strncasecmp("CHAT ", p, 5))
1192 {
1193 p += 5;
1194 if (!*p)
1195 return -1;
1196 while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)
1197 || dtmp->pass))
1198 dtmp = dtmp->nexts;
1199 if (dtmp)
1200 {
1201 if (option(dtmp->sopt, S_CONNECTING)
1202 || option(dtmp->sopt, S_CONNECTED))
1203 {
1204 sic_slog(LOG_CLIENT, "--- Connection already initiated.");
1205 return 0;
1206 }
1207 sic_connect(dtmp);
1208 }
1209 else
1210 {
1211 dtmp = (struct server_ *) malloc(sizeof(struct server_));
1212 bzero(dtmp, sizeof(struct server_));
1213 sic_listen(dtmp);
1214 if (dtmp->fd < 0)
1215 free(dtmp);
1216 else
1217 {
1218 dtmp->nick = strdup(p);
1219 vsic_write("PRIVMSG %s :\001DCC CHAT chat %lu %u\001", p,
1220 htonl(inet_addr(dtmp->ip)), htons(dtmp->port));
1221 vsic_slog(LOG_CLIENT, "--- DCC chat offered to %s", p);
1222 dtmp->nexts = slist;
1223 slist = dtmp;
1224 }
1225 }
1226 return 0;
1227 }
1228 if (!strncasecmp("SEND ", p, 5))
1229 {
1230 char *file = index(p+5, ' '), *basename;
1231
1232 if (file == NULL)
1233 return -1;
1234 *file++ = '\0';
1235 if ((basename = rindex(file, '/')) == NULL)
1236 basename = file;
1237 else
1238 basename++;
1239 p += 5;
1240
1241 dtmp = (struct server_ *) malloc(sizeof(struct server_));
1242 bzero(dtmp, sizeof(struct server_));
1243
1244 if ((dtmp->sendf = open(file, O_RDONLY)) < 0)
1245 {
1246 free(dtmp);
1247 vsic_slog(LOG_CLIENT, "--- Unable to open %s: %s", file,
1248 strerror(errno));
1249 return 0;
1250 }
1251 dtmp->pass = strdup(file);
1252
1253 sic_listen(dtmp);
1254 if (dtmp->fd < 0)
1255 {
1256 close(dtmp->sendf);
1257 free(dtmp->pass);
1258 free(dtmp);
1259 }
1260 else
1261 {
1262 struct stat st;
1263
1264 if (fstat(dtmp->sendf, &st) < 0)
1265 {
1266 vsic_slog(LOG_CLIENT, "--- Unable to find the file size: %s",
1267 strerror(errno));
1268 close(dtmp->fd);
1269 close(dtmp->sendf);
1270 free(dtmp->pass);
1271 free(dtmp);
1272 return 0;
1273 }
1274 dtmp->size = st.st_size;
1275 dtmp->nick = strdup(p);
1276 vsic_write("PRIVMSG %s :\001DCC SEND %s %lu %u %u\001", p, basename,
1277 htonl(inet_addr(dtmp->ip)), htons(dtmp->port),st.st_size);
1278 vsic_slog(LOG_CLIENT, "--- \"%s\" offered to %s", file, p);
1279 dtmp->nexts = slist;
1280 slist = dtmp;
1281 }
1282 return 0;
1283 }
1284 if (!strncasecmp("GET ", p, 4))
1285 {
1286 char *path = index(p+4, ' '), *file;
1287
1288 if (path == NULL)
1289 return -1;
1290 p += 4;
1291 *path++ = '\0';
1292 if (file = rindex(path, '/'))
1293 file++;
1294 else
1295 file = path;
1296 while (dtmp
1297 && (dtmp->pass == NULL || !rmatch(file, dtmp->pass)
1298 || !option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)))
1299 dtmp = dtmp->nexts;
1300 if (dtmp)
1301 {
1302 if (option(dtmp->sopt, S_CONNECTING)
1303 || option(dtmp->sopt, S_CONNECTED))
1304 {
1305 sic_slog(LOG_CLIENT, "--- Connection already initiated.");
1306 return 0;
1307 }
1308 sic_connect(dtmp);
1309 dtmp->ts = time(NULL);
1310 }
1311 else
1312 return -4; /* invalid parameter */
1313 return 0;
1314 }
1315 if (!strncasecmp("delete ", p, 7))
1316 {
1317 char *file = index(p+7, ' ');
1318
1319 p += 7;
1320 if (file)
1321 *file++ = '\0';
1322 while (dtmp
1323 && ((!option(dtmp->sopt, S_DCC) && !option(dtmp->sopt, S_LISTEN))
1324 || strcasecmp(dtmp->nick, p)
1325 || (file && !rmatch(file, dtmp->pass))))
1326 dtmp = dtmp->nexts;
1327 if (dtmp)
1328 {
1329 if (option(dtmp->sopt, S_CONNECTING)
1330 || option(dtmp->sopt, S_CONNECTED))
1331 {
1332 sic_slog(LOG_CLIENT, "--- Closing DCC.");
1333 close(dtmp->fd);
1334 }
1335 sic_dserver(dtmp);
1336 }
1337 else
1338 sic_slog(LOG_CLIENT, "--- No such DCC.");
1339 return 0;
1340 }
1341 return -1;
1342 }
1343