/* * server.c: interactions with servers, tcp code, and DNS resolver * * Copyright(c) 1997-2000 - All Rights Reserved * * See the COPYRIGHT file. */ #ifndef lint static char rcsid[] = "@(#)$Id: server.c,v 1.83 2001/08/01 01:21:42 kalt Exp $"; #endif #include "os.h" #if HAVE_SYS_FILIO_H # include #endif #include "struct.h" #include "term.h" #include "window.h" #include "option.h" #include "config.h" #include "utils.h" #include "server.h" extern char need_collect; extern struct window_ *current; extern void sic_format(char *, char *, char *, char *); static struct server_ *slist = NULL; struct server_ *server = NULL; static int sic_nextaddr(srv) struct server_ *srv; { #if defined(HAVE_GETADDRINFO) struct addrinfo *last; assert(srv->address); assert(srv->ip); last = srv->addrip; do { srv->addrip = srv->addrip->ai_next; if (srv->addrip == NULL) srv->addrip = srv->address; } while (srv->addrip->ai_family != PF_INET && srv->addrip->ai_family != PF_INET6); free(srv->ip); srv->ip = aitoip(srv->addrip); vsic_slog(LOG_DEBUG, "sic_nextaddr: srv->addrip %x, last %x", srv->addrip, last); if (srv->addrip != last) return 1; #endif vsic_slog(LOG_DEBUG, "sic_nextaddr returns 0"); return 0; } /* sic_connect: connect() to a server (given a struct server_ pointer) */ void sic_connect(srv) struct server_ *srv; { struct sockaddr_in sock_sin; int fd, opt = 1, rc; char *vif; assert (srv->ip); /* even if srv->addrip is defined */ bzero((char *)&sock_sin, sizeof(sock_sin)); sock_sin.sin_family = AF_INET; #if defined(HAVE_GETADDRINFO) if (srv->addrip) fd = socket(srv->addrip->ai_family, SOCK_STREAM, srv->addrip->ai_protocol); else #endif fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno)); if (fd > 0 && (vif = getenv("SICIP"))) { /* the following should be fixed rather than simply reporting an error */ sock_sin.sin_addr.s_addr = inet_addr(vif); if (sock_sin.sin_addr.s_addr == INADDR_NONE) vsic_slog(LOG_CLIENT, "--- Bad IP address: SICIP = %s", vif); else if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0) vsic_slog(LOG_CLIENT, "--- Unable to bind to IP %s: %s", vif, strerror(errno)); } if (fd > 0 && ioctl(fd, FIONBIO, &opt) < 0) vsic_slog(LOG_CLIENT, "--- ioctl() for %s:%d failed: %s", srv->sname, srv->port, strerror(errno)); #if defined(HAVE_GETADDRINFO) if (srv->addrip) rc = connect(fd, srv->addrip->ai_addr, srv->addrip->ai_addrlen); else #endif { sock_sin.sin_port = htons(srv->port); sock_sin.sin_addr.s_addr = inet_addr(srv->ip); rc = connect(fd, (struct sockaddr *)&sock_sin, sizeof(sock_sin)); } if (fd > 0 && rc < 0 && errno != EINPROGRESS) { rc = errno; vsic_slog(LOG_CLIENT, "--- Connection to %s:%d failed: %s", srv->sname, srv->port, strerror(errno)); close(fd); if (rc == EHOSTUNREACH && !option(srv->sopt, S_DCC)) fd = -2; else fd = -1; } unset_option(srv->sopt, S_RECONNECT); unset_option(srv->sopt, S_QUIT); switch (fd) { case -2: /* no route to host, may be there's another address to try? */ unset_option(srv->sopt, S_CONNECTING); if (sic_nextaddr(srv)) { set_option(srv->sopt, S_RECONNECT); srv->ts = time(NULL) - 4; break; } srv->ts = time(NULL); break; case -1: /* other type of failures */ unset_option(srv->sopt, S_CONNECTING); if (!option(srv->sopt, S_DCC)) /* needed? */ srv->ts = time(NULL); break; default: /* connection in progress */ set_option(srv->sopt, S_CONNECTING); if (!option(srv->sopt, S_CONFIG)) cfg_read(srv->sname); set_option(srv->sopt, S_CONFIG); break; } srv->fd = fd; srv->lastr = 0; srv->readbuf[0] = '\0'; } /* sic_listen: create a new socket, and bind it */ static void sic_listen(dcc) struct server_ *dcc; { struct sockaddr_in sock_sin; int fd, sz = sizeof(struct sockaddr_in); char *vif; if (getsockname(server->fd, (struct sockaddr *)&sock_sin, &sz) < 0) { vsic_slog(LOG_CLIENT, "--- Unable to get my own IP: %s",strerror(errno)); dcc->fd = -1; return; } else sz = sizeof(struct sockaddr_in); dcc->ip = strdup((char *)inet_ntoa(sock_sin.sin_addr)); bzero((char *)&sock_sin, sizeof(sock_sin)); sock_sin.sin_family = AF_INET; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno)); if (vif = getenv("SICIP")) sock_sin.sin_addr.s_addr = inet_addr(vif); else sock_sin.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0) { vsic_slog(LOG_CLIENT, "--- bind() call failed: %s", strerror(errno)); close(fd); fd = -1; } if (fd > 0 && listen(fd, 1) < 0) { vsic_slog(LOG_CLIENT, "--- listen() call failed: %s", strerror(errno)); close(fd); fd = -1; } if (fd > 0) if (getsockname(fd, (struct sockaddr *)&sock_sin, &sz) < 0) { vsic_slog(LOG_CLIENT, "--- getsockname() call failed: %s", strerror(errno)); free(dcc->ip); close(fd); } else { dcc->port = sock_sin.sin_port; dcc->sname = strdup(server->sname); set_option(dcc->sopt, S_LISTEN); } dcc->fd = fd; } /* select_server: set a different server */ struct server_ * select_server(name) char *name; { struct server_ *stmp = slist; assert(name); while (stmp && (strcasecmp(stmp->sname, name) || !option(stmp->sopt, S_CONNECTED))) stmp = stmp->nexts; return server = stmp; } /* sic_server: adds a server to the list, does NOT connect */ struct server_ * sic_server(nick, name, port, pass) char *nick, *name, *pass; int port; { struct server_ **stmp = &slist; while (*stmp) stmp = &((*stmp)->nexts); server = (struct server_ *) malloc(sizeof(struct server_)); bzero(server, sizeof(struct server_)); *stmp = server; server->nick = strdup(nick); server->sname = strdup(name); server->port = port; if (pass) server->pass = strdup(pass); #if defined(HAVE_GETADDRINFO) { struct addrinfo *address, hints; char port_str[10]; bzero((char *)&hints, sizeof(hints)); hints.ai_flags = AI_CANONNAME|AI_NUMERICHOST; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; sprintf(port_str, "%d", port); if (getaddrinfo(name, port_str, &hints, &address) == 0) { server->ip = strdup(name); server->address = address; server->addrip = address; } } #else if (inet_addr(name) != INADDR_NONE) server->ip = strdup(name); #endif if (server->ip == NULL) { dns_lookup(name, server); set_option(server->sopt, S_DNS); } set_option(server->sopt, S_NOTCONNECTED); set_option(server->sopt, S_QUIT); server->fd = -1; return server; } /* sic_dserver: removes a server from the list */ void sic_dserver(del) struct server_ *del; { struct server_ **stmp = &slist; assert(sic_scnt(del) == 0); assert(option(del->sopt, S_CONNECTED) == 0); assert(option(del->sopt, S_DNS) == 0); while (*stmp != del) stmp = &((*stmp)->nexts); *stmp = del->nexts; opt_free(&(del->custs)); free(del->sname); if (del->ip) free(del->ip); #if defined(HAVE_GETADDRINFO) if (del->address) freeaddrinfo(del->address); #endif if (del->pass) free(del->pass); free(del->nick); if (del->uname) free(del->uname); if (del->rname) free(del->rname); if (del->readf) close(del->readf); if (del->sendf) close(del->sendf); free(del); } static void sic_write(str) char *str; { if (server && server->fd >= 0) { if (write(server->fd, str, strlen(str)) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) /* should be dealt with in a better way, not worth my time unless it happens */ vsic_slog(LOG_CLIENT, "--- write() to server failed: %s", strerror(errno)); str[strlen(str)-1] = '\0'; /* hmmpf */ sic_slog(LOG_OSNIF, str); } else sic_slog(LOG_CLIENT, "--- No server"); } void vsic_write(char *format, ...) { char buffer[1024]; va_list va; va_start(va, format); vsprintf(buffer, format, va); va_end(va); strcat(buffer, "\n"); sic_write(buffer); } static int readln(fd, buffer, length) int fd, length; char *buffer; { int status, ptr; ptr = -1; while (buffer[++ptr]); while (((status = read(fd, &buffer[ptr], 1)) > 0) && (ptr < length - 1)) { if (buffer[ptr++] == '\n') break; } if (status == -1 && (errno == EWOULDBLOCK || errno == EAGAIN)) { /* incomplete line */ buffer[ptr] = '\0'; return -2; } if (status > 0) { if (buffer[ptr-2] == '\r') buffer[ptr-2] = '\0'; else buffer[ptr-1] = '\0'; sic_slog(LOG_ISNIF, buffer); } return (status); } /* this function is totally buggy, but it's simple and works most of the time*/ static void parse(str) char *str; { char *sender, *cmd, *dest, *para; if (option(server->sopt, S_DCC)) { char snder[267]; sprintf(snder, "%s@%s", server->nick, server->sname); sic_format(snder, NULL, NULL, str); return; } if (*str == ':') { sender = ++str; while (*++str != ' '); *str++ = '\0'; } else sender = server->sname; if (!strncmp(str, "ERROR ", 6)) { cmd = "ERROR"; dest = server->nick; para = str; } else { cmd = str; while (*++str != ' '); *str++ = '\0'; if (*str == ':') { vsic_slog(LOG_DEBUG, "dest begins with : for `%s %s %s'",sender,cmd,str); dest = str+1; para = ""; } else { dest = str; while (*++str != ' ' && *str); if (*str) { *str++ = '\0'; if (*str == ':') para = str + 1; else para = str; } else para = ""; } } if (strcmp(cmd, "PONG")) sic_format(sender, cmd, dest, para); } /* sic_select: big fat io loop */ int sic_select() { fd_set rfd, wfd, efd; int n, maxfd = 0; struct server_ *stmp = slist; struct timeval timeout; char buf[10240]; FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd); timeout.tv_usec = 0; timeout.tv_sec = 5; FD_SET(0, &rfd); while (stmp) { if (stmp->fd > maxfd) maxfd = stmp->fd; server = stmp; if (option(stmp->sopt, S_NOTCONNECTED) && !option(stmp->sopt, S_CONNECTING) && !option(stmp->sopt, S_CONNECTED) && !option(stmp->sopt, S_QUIT) && !option(stmp->sopt, S_DCC) && (time(NULL) - stmp->ts) >= 1800 && get_option(Z_RECONNECT, NULL)) { select_active(NULL, 0); vsic_slog(LOG_CLIENT, "--- Attempting to reconnect to %s:%d", stmp->sname, stmp->port); set_option(stmp->sopt, S_RECONNECT); } if (option(stmp->sopt, S_RECONNECT) && (time(NULL) - stmp->ts) >= 5) { select_active(NULL, 0); sic_connect(stmp); } if (option(stmp->sopt, S_CONNECTED)) { if (!option(stmp->sopt, S_DCC)) { server = stmp; select_active(NULL, 0); if (stmp->lastr && time(NULL) - stmp->lastr > 60) { if ((time(NULL) - stmp->lastr) % 120 < 60) { if (!option(stmp->sopt, S_PING)) { vsic_write("PING ."); set_option(stmp->sopt, S_PING); } } else unset_option(stmp->sopt, S_PING); } if (need_collect) collect_channel(); } FD_SET(stmp->fd, &rfd); } if (option(stmp->sopt, S_SEND) && stmp->sent < stmp->size) FD_SET(stmp->fd, &wfd); if (option(stmp->sopt, S_LISTEN)) FD_SET(stmp->fd, &rfd); if (option(stmp->sopt, S_CONNECTING) && !option(stmp->sopt, S_DNS)) FD_SET(stmp->fd, &wfd); stmp = stmp->nexts; } display_status(); term_input(NULL, 0); term_flush(); #if defined(GNU_PTH) n = pth_select(maxfd + 1, &rfd, &wfd, NULL, &timeout); #else n = select(maxfd + 1, &rfd, &wfd, &efd, &timeout); #endif if (n < 0) return 0; stmp = slist; while (stmp) { /* first deal with pending DCCs */ if (stmp->fd >= 0 && option(stmp->sopt, S_LISTEN)) { if (FD_ISSET(stmp->fd, &rfd)) { struct sockaddr_in sock_sin; int newfd, sz = sizeof(struct sockaddr_in); server = stmp; unset_option(stmp->sopt, S_LISTEN); set_option(stmp->sopt, S_DCC); select_active(NULL, 0); if ((newfd=accept(stmp->fd,(struct sockaddr *)&sock_sin,&sz))<0) { vsic_slog(LOG_CLIENT, "DCC accept() failed for %s@%s: %s", stmp->nick, inet_ntoa(sock_sin.sin_addr), strerror(errno)); stmp = stmp->nexts; sic_dserver(server); } else { close(stmp->fd); stmp->fd = newfd; free(stmp->sname); stmp->sname = strdup((char *)inet_ntoa(sock_sin.sin_addr)); set_option(stmp->sopt, S_CONNECTED); vsic_slog(LOG_CLIENT, "--- DCC connection received from %s@%s", stmp->nick, stmp->sname); dns_lookup(stmp->sname, NULL); sz = 1; if (ioctl(stmp->fd, FIONBIO, &sz) < 0) vsic_slog(LOG_CLIENT, "--- ioctl() failed: %s", strerror(errno)); if (stmp->pass) set_option(stmp->sopt, S_SEND); stmp->ts = time(NULL); } } } /* DCC sends */ if (option(stmp->sopt, S_SEND)) { server = stmp; select_active(NULL, 0); if (FD_ISSET(stmp->fd, &rfd)) { u_32int_t total; /* DCC ack */ if (recv(stmp->fd, (char *) &total, sizeof(u_32int_t),0) > 0) stmp->read = ntohl(total); else { vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost: %s", stmp->pass, stmp->nick, strerror(errno)); vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).", stmp->read, stmp->size, (int)(100*stmp->read/stmp->size)); close(stmp->fd); stmp = stmp->nexts; sic_dserver(server); continue; } if (stmp->read == stmp->size) { vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s completed in %s (%ub/s)", stmp->pass, stmp->nick, sic_tdiff(time(NULL)-stmp->ts, 0), stmp->size/(1+time(NULL)-stmp->ts)); shutdown(stmp->fd, 2); close(stmp->fd); stmp = stmp->nexts; sic_dserver(server); continue; } } if (FD_ISSET(stmp->fd, &wfd) && stmp->sent - stmp->read < 10240) { char error = 0; unsigned long rd, st = 0; if (lseek(stmp->sendf, stmp->sent, SEEK_SET) < 0) error = 1; if ((rd = read(stmp->sendf, buf, 10240)) < 0) error = 2; if (rd > 0 && ((st = write(stmp->fd, buf, rd)) < 0)) error = 3; if (error) { if (error != 3) vsic_slog(LOG_CLIENT, "--- %s() failed: %s!", (error == 1) ?"lseek":"read",strerror(errno)); vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost.", stmp->pass, stmp->nick); vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).", stmp->read, stmp->size, (int)(100*stmp->read/stmp->size)); close(stmp->fd); stmp = stmp->nexts; sic_dserver(server); continue; } stmp->sent += st; } stmp = stmp->nexts; continue; } /* DCC chat, DCC get, and normal server traffic */ if (stmp->fd >= 0 && !option(stmp->sopt, S_LISTEN)) { server = stmp; select_active(NULL, 0); /* default window for this server */ if (FD_ISSET(stmp->fd, &rfd)) { int sz; if (option(stmp->sopt, S_DCC) && stmp->pass) sz = read(stmp->fd, buf, 8192); /* big buffer for files */ else { /*read up to \r\n otherwise*/ unset_option(stmp->sopt, S_PING); sz = readln(stmp->fd, stmp->readbuf, 520); } if (sz > 0) { stmp->lastr = time(NULL); if (option(stmp->sopt, S_NOTCONNECTED)) { assert(!option(stmp->sopt, S_DCC)); unset_option(stmp->sopt, S_NOTCONNECTED); sic_slog(LOG_CLIENT, "--- Connection established."); } if (option(stmp->sopt, S_DCC) && stmp->pass) { /* file transfer */ if (stmp->readf == 0) stmp->readf = open(stmp->pass, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (stmp->readf == 0) { vsic_slog(LOG_CLIENT, "--- Unable to create file \"%s\"!", stmp->pass); close(stmp->fd); stmp = stmp->nexts; sic_dserver(server); /* "continue;" below */ } else { u_32int_t total; /* DCC ack */ write(stmp->readf, buf, sz); /* ugly! */ total = htonl(stmp->read += sz); send(stmp->fd, (char *) &total, sizeof(u_32int_t),0); if (stmp->read == stmp->size) /* got all of it? */ { stmp->size /= 1024; vsic_slog(LOG_CLIENT, "--- DCC \"%s\" from %s completed in %s (%ub/s)", stmp->pass, stmp->nick, sic_tdiff(time(NULL)-stmp->ts, 0), stmp->size/(1+time(NULL)-stmp->ts)); shutdown(stmp->fd, 2); close(stmp->fd); stmp = stmp->nexts; sic_dserver(server); /* "continue;" below */ } else stmp = stmp->nexts; } continue; } else { /* readln() */ if (!option(stmp->sopt, S_DCC) && !strncmp("PING ", stmp->readbuf, 5)) vsic_write("PONG %s :%s", server->nick, (stmp->readbuf[4] && stmp->readbuf[5]) ? stmp->readbuf+6 : ""); else { /* current window for this server */ select_active(NULL, 1); parse(stmp->readbuf); } stmp->readbuf[0] = '\0'; } } else if (sz == -2) { vsic_slog(LOG_DEBUG, "Incomplete line from %s", stmp->sname); } else { close(stmp->fd); stmp->fd = -1; if (option(stmp->sopt, S_NOTCONNECTED)) { /* this can happen when connecting locally */ assert(!option(stmp->sopt, S_DCC)); vsic_slog(LOG_CLIENT, "--- Connection to %s %d [%s] failed.", stmp->sname, stmp->port, stmp->ip); } set_option(stmp->sopt, S_NOTCONNECTED); unset_option(stmp->sopt, S_CONNECTED); if ((time(NULL) - stmp->ts) > 60 && !option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_QUIT) && get_option(Z_RECONNECT, NULL)) set_option(stmp->sopt, S_RECONNECT); stmp->ts = time(NULL); if (option(stmp->sopt, S_DCC)) { vsic_slog(LOG_CLIENT, "--- DCC to %s@%s lost.", stmp->nick, stmp->sname); if (stmp->pass) { vsic_slog(LOG_CLIENT, "--- Got %lu out of %lu (%u%% of %s).", stmp->read, stmp->size, (int)(100*stmp->read/stmp->size), stmp->pass); stmp = stmp->nexts; sic_dserver(server); } } else vsic_slog(LOG_CLIENT, "--- Lost connection to %s:%d : %s", stmp->sname, stmp->port, option(stmp->sopt, S_RECONNECT) ? "Reconnecting..." : option(stmp->sopt, S_QUIT) || !get_option(Z_RECONNECT, NULL) ? "No reconnect.":"Will attempt to reconnect."); continue; } } /* connection establishment */ if (FD_ISSET(stmp->fd, &wfd)) { char *wp, buffer[512]; unset_option(stmp->sopt, S_CONNECTING); if (!option(stmp->sopt, S_DCC)) { server->umode[0] = '\0'; sprintf(buffer, "%s%s%sNICK %s\nUSER %s myhost 0 :%s\n", (server->pass) ? "PASS " : "", (server->pass) ? server->pass : "", (server->pass) ? "\n" : "", stmp->nick, (stmp->uname) ? stmp->uname : (wp = getenv("SICUSER")) ? wp : (wp = getenv("USER")) ? wp : "sicuser", (stmp->rname) ? stmp->rname : (wp = getenv("SICNAME")) ? wp :"wasting time"); if (write(stmp->fd, buffer, strlen(buffer)) == -1) { close(stmp->fd); stmp->fd = -1; stmp->ts = time(NULL); set_option(stmp->sopt, S_NOTCONNECTED); sic_slog(LOG_CLIENT, "--- Connection failed."); } else { del_member(NULL, NULL); set_option(stmp->sopt, S_CONNECTED); } } else { vsic_slog(LOG_CLIENT, "--- DCC to %s@%s established.", stmp->nick, stmp->sname); set_option(stmp->sopt, S_CONNECTED); unset_option(stmp->sopt, S_NOTCONNECTED); } } } stmp = stmp->nexts; } if (FD_ISSET(0, &rfd)) return 1; else return 0; } int cmd_server(p) char *p; { struct server_ *stmp = slist; int n = 0; if (p == NULL) { /* NULL argument, this is only used internally */ if (server == NULL) return 0; if (option(server->sopt, S_CONNECTING) || option(server->sopt, S_CONNECTED)) { if (option(server->sopt, S_QUIT)) { if (server->fd >= 0) close(server->fd); server->fd = -1; set_option(server->sopt, S_NOTCONNECTED); unset_option(server->sopt, S_CONNECTING); unset_option(server->sopt, S_CONNECTED); vsic_slog(LOG_CLIENT, "--- Closed connection to %s:%d", server->sname, server->port); } } unset_option(server->sopt, S_RECONNECT); set_option(server->sopt, S_QUIT); return 0; } /* real user input */ if (!*p || !strcasecmp(p, "-v")) { /* no argument or just `-v', user is simply requesting a list */ sic_slog(LOG_CLIENT, "--- Server list:"); while (stmp) { if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)) vsic_slog(LOG_CLIENT, "--- %2d: %s %d %s(%s%s)%s", n++, stmp->sname, stmp->port, (stmp->pass) ? "*password* " : "", (option(stmp->sopt, S_CONNECTING)) ? "will be " : (option(stmp->sopt, S_CONNECTED)) ? "" : "was ", stmp->nick, (stmp->ip) ? "" : option(stmp->sopt, S_DNS) ? " Looking up address" : " Unknown address"); if (*p && stmp->ip) #if defined(HAVE_GETADDRINFO) { struct addrinfo *address = stmp->address; vsic_slog(LOG_CLIENT, "--- => %s[%s]", (stmp->address && stmp->address->ai_canonname) ? stmp->address->ai_canonname : "???", stmp->ip); if (address && address->ai_next) while (address) { if (address->ai_family == PF_INET || address->ai_family == PF_INET6) vsic_slog(LOG_CLIENT, "--- > %s[%s]", (address->ai_canonname) ? address->ai_canonname : "???", aitoip(address)); address = address->ai_next; } } #else vsic_slog(LOG_CLIENT, "--- => [%s]", stmp->ip); #endif stmp = stmp->nexts; } } else if (!strncasecmp(p, "-d", 2)) { char *number = p; while (*number && !isdigit(*number)) number += 1; if (*number == '\0') return -4; n = 0; while (stmp) { if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN) && n++ == atoi(number)) break; stmp = stmp->nexts; } if (stmp == NULL) return -4; if (option(stmp->sopt, S_DNS)) { sic_slog(LOG_CLIENT, "--- DNS lookup in progress, cannot delete server. (try again)"); return 0; } if (option(stmp->sopt, S_CONNECTED)) { sic_slog(LOG_CLIENT, "--- Close connection first."); return 0; } if (n = sic_scnt(stmp)) { vsic_slog(LOG_CLIENT,"--- %d window(s) are bound to this server.",n); return 0; } assert(server != stmp); /* this shouldn't be possible? */ vsic_slog(LOG_CLIENT,"--- Deleted server %d (%s).", atoi(number), stmp->sname); sic_dserver(stmp); return 0; } else { char *number = p; if (*p == '+') number = p+1; while (*number) if (!isdigit(*number++)) { number = NULL; break; } if (number) { /* argument is a number, find selected server and try to connect */ if (current->via && (option(current->via->sopt, S_CONNECTED) || option(current->via->sopt, S_CONNECTING))) { sic_slog(LOG_CLIENT, "--- This window is already active."); return 0; } while (stmp) { if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN) && n++ == atoi(p)) break; stmp = stmp->nexts; } if (stmp) { server = stmp; if (stmp->ip || option(stmp->sopt, S_DNS)) { if (sic_swin(1)) { if (!option(stmp->sopt, S_CONNECTED)) { set_option(stmp->sopt, S_CONNECTING); if (option(stmp->sopt, S_DNS)) { vsic_slog(LOG_CLIENT, "--- Connection to %s pending DNS lookup.", stmp->sname); } else { if (*p == '+') sic_nextaddr(stmp); vsic_slog(LOG_CLIENT, "--- Connecting to %s %d [%s]", stmp->sname, stmp->port, stmp->ip); sic_connect(stmp); } } } } else vsic_slog(LOG_CLIENT, "--- Unknown IP for %s", stmp->sname); } else sic_slog(LOG_CLIENT, "--- No such server."); } else { /* format: /server name port pass */ char *port, *pass = NULL; int portn = 6667; if (port = index(p, ' ')) { *port++ = '\0'; if (pass = index(port, ' ')) *pass++ = '\0'; portn = atoi(port); } sic_server((current->via) ? current->via->nick : "sic", p, portn, pass); cmd_server(""); } } return 0; } /* * DCC code */ static void sic_dwrite(nick, str) char *nick, *str; { struct server_ *dtmp = slist; while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, nick))) dtmp = dtmp->nexts; if (dtmp) if (dtmp->fd >= 0) { if (write(dtmp->fd, str, strlen(str)) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) /* should be dealt with in a better way, not worth my time unless it happens */ vsic_slog(LOG_CLIENT, "--- write() to DCC failed: %s", strerror(errno)); str[strlen(str)-1] = '\0'; /* hmmpf */ sic_slog(LOG_OSNIF, str); } else sic_slog(LOG_CLIENT, "--- DCC not established."); else sic_slog(LOG_CLIENT, "--- No such DCC."); } void vsic_dwrite(char *nick, char *format, ...) { char buffer[1024]; va_list va; va_start(va, format); vsprintf(buffer, format, va); va_end(va); strcat(buffer, "\n"); sic_dwrite(nick, buffer); } /* sic_dcc: called to deal with DCC requests * 0 -> chat , 1 -> send */ void sic_dcc(type, nick, ip, port, file, size) int type; char *nick, *file; unsigned long ip, size; unsigned int port; { struct server_ *dtmp = slist; unsigned long lip; assert(type == 0 || type == 1); while (dtmp) { if (option(dtmp->sopt, S_DCC) && !strcasecmp(dtmp->nick, nick) && (!dtmp->pass || !strcmp(dtmp->pass, file))) break; dtmp = dtmp->nexts; } if (dtmp) { /* should be dealt with more gracefully */ if ((option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED)) && (type == 0 || (dtmp->pass && !strcmp(dtmp->pass, file)))) { vsic_slog(LOG_CLIENT, "--- DCC chat request from %s ignored (duplicate).", nick); return; } vsic_slog(LOG_CLIENT, "--- old DCC chat request from %s deleted.", nick); sic_dserver(dtmp); } if (type == 1 && size == 0) { vsic_slog(LOG_CLIENT, "--- Invalid DCC request (file size = 0) from %s.", nick); return; } dtmp = (struct server_ *) malloc(sizeof(struct server_)); bzero(dtmp, sizeof(struct server_)); dtmp->fd = -1; dtmp->ts = time(NULL); set_option(dtmp->sopt, S_DCC); dtmp->nick = strdup(nick); if (type == 1) { dtmp->pass = strdup(file); dtmp->size = size; } lip = ntohl(ip); dtmp->sname = strdup((char *)inet_ntoa(*(struct in_addr*)&lip)); dtmp->ip = strdup((char *)inet_ntoa(*(struct in_addr*)&lip)); set_option(dtmp->sopt, S_NOTCONNECTED); dns_lookup(dtmp->ip, NULL); switch (type) { case 0: vsic_slog(LOG_CLIENT, "--- DCC CHAT request from %s@%s.", dtmp->nick, dtmp->ip); break; case 1: vsic_slog(LOG_CLIENT, "--- DCC SEND request from %s@%s: %s %lu", dtmp->nick, dtmp->ip, file, size); break; default: abort(); /* never */ } if ((dtmp->port = port) < 1024) vsic_slog(LOG_CLIENT, "--- DCC request from a privileged port %d!",port); dtmp->nexts = slist; slist = dtmp; } int cmd_dcc(p) char *p; { struct server_ *dtmp = slist; if (!*p) { sic_slog(LOG_CLIENT, "--- DCC list:"); while (dtmp) { if (option(dtmp->sopt, S_DCC)) if (dtmp->pass) vsic_slog(LOG_CLIENT, "--- %s@%s:%d %s [%lu] %s %s: %d%% %s", dtmp->nick, dtmp->sname, dtmp->port, dtmp->pass, dtmp->size, (option(dtmp->sopt, S_CONNECTED)) ? "active" : (option(dtmp->sopt, S_CONNECTING)) ? "connecting" : "not connected", (option(dtmp->sopt,S_SEND)) ? "Sending":"Reading", (int)(100*dtmp->read/dtmp->size), sic_tdiff(time(NULL)-dtmp->ts, 0)); else vsic_slog(LOG_CLIENT, "--- %s@%s:%d %s", dtmp->nick, dtmp->sname, dtmp->port, (option(dtmp->sopt, S_CONNECTED)) ? "active" : (option(dtmp->sopt, S_CONNECTING)) ? "connecting" : "not connected"); if (option(dtmp->sopt, S_LISTEN)) vsic_slog(LOG_CLIENT, "--- %s@%s pending %s%s", dtmp->nick, dtmp->sname, (dtmp->pass) ? "Send: " : "chat", (dtmp->pass) ? dtmp->pass : ""); dtmp = dtmp->nexts; } return 0; } if (server == NULL) return -5; if (!strncasecmp("CHAT ", p, 5)) { p += 5; if (!*p) return -1; while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p) || dtmp->pass)) dtmp = dtmp->nexts; if (dtmp) { if (option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED)) { sic_slog(LOG_CLIENT, "--- Connection already initiated."); return 0; } sic_connect(dtmp); } else { dtmp = (struct server_ *) malloc(sizeof(struct server_)); bzero(dtmp, sizeof(struct server_)); sic_listen(dtmp); if (dtmp->fd < 0) free(dtmp); else { dtmp->nick = strdup(p); vsic_write("PRIVMSG %s :\001DCC CHAT chat %lu %u\001", p, htonl(inet_addr(dtmp->ip)), htons(dtmp->port)); vsic_slog(LOG_CLIENT, "--- DCC chat offered to %s", p); dtmp->nexts = slist; slist = dtmp; } } return 0; } if (!strncasecmp("SEND ", p, 5)) { char *file = index(p+5, ' '), *basename; if (file == NULL) return -1; *file++ = '\0'; if ((basename = rindex(file, '/')) == NULL) basename = file; else basename++; p += 5; dtmp = (struct server_ *) malloc(sizeof(struct server_)); bzero(dtmp, sizeof(struct server_)); if ((dtmp->sendf = open(file, O_RDONLY)) < 0) { free(dtmp); vsic_slog(LOG_CLIENT, "--- Unable to open %s: %s", file, strerror(errno)); return 0; } dtmp->pass = strdup(file); sic_listen(dtmp); if (dtmp->fd < 0) { close(dtmp->sendf); free(dtmp->pass); free(dtmp); } else { struct stat st; if (fstat(dtmp->sendf, &st) < 0) { vsic_slog(LOG_CLIENT, "--- Unable to find the file size: %s", strerror(errno)); close(dtmp->fd); close(dtmp->sendf); free(dtmp->pass); free(dtmp); return 0; } dtmp->size = st.st_size; dtmp->nick = strdup(p); vsic_write("PRIVMSG %s :\001DCC SEND %s %lu %u %u\001", p, basename, htonl(inet_addr(dtmp->ip)), htons(dtmp->port),st.st_size); vsic_slog(LOG_CLIENT, "--- \"%s\" offered to %s", file, p); dtmp->nexts = slist; slist = dtmp; } return 0; } if (!strncasecmp("GET ", p, 4)) { char *path = index(p+4, ' '), *file; if (path == NULL) return -1; p += 4; *path++ = '\0'; if (file = rindex(path, '/')) file++; else file = path; while (dtmp && (dtmp->pass == NULL || !rmatch(file, dtmp->pass) || !option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p))) dtmp = dtmp->nexts; if (dtmp) { if (option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED)) { sic_slog(LOG_CLIENT, "--- Connection already initiated."); return 0; } sic_connect(dtmp); dtmp->ts = time(NULL); } else return -4; /* invalid parameter */ return 0; } if (!strncasecmp("delete ", p, 7)) { char *file = index(p+7, ' '); p += 7; if (file) *file++ = '\0'; while (dtmp && ((!option(dtmp->sopt, S_DCC) && !option(dtmp->sopt, S_LISTEN)) || strcasecmp(dtmp->nick, p) || (file && !rmatch(file, dtmp->pass)))) dtmp = dtmp->nexts; if (dtmp) { if (option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED)) { sic_slog(LOG_CLIENT, "--- Closing DCC."); close(dtmp->fd); } sic_dserver(dtmp); } else sic_slog(LOG_CLIENT, "--- No such DCC."); return 0; } return -1; }