#include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #ifdef TIME_WITH_SYS_TIME #include #endif #else #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SSL #include #include #include #include #include #include #endif #include "sbuf.h" #include "struct.h" #include "send.h" #include "ctcp.h" #define S_READ 1 #define S_WRITE 2 #define S_EXCEPT 4 struct selectfds { int fd_setsize; size_t size; int nfds; fd_set *rfds; fd_set *wfds; fd_set *efds; }; extern int h_errno; extern char logbuf[]; extern int bnclog (confetti * jr, char *logbuff); extern void add_access (confetti *, accesslist *); extern int handlepclient (struct cliententry *cptr, int fromwho, int pargc, char **pargv, char *prefix); extern void bnckill (int reason); extern int wipechans(struct cliententry *cptr); extern int remnl (char *buf, int size); extern void *pmalloc(size_t size); extern char *helplist[]; extern char *helplista[]; extern unsigned char motdb[]; int a_sock = 0; int s_sock = 0; struct sockaddr_in muhsin; struct sockaddr wipi; int sinlen; unsigned char allbuf[PACKETBUFF+1]; unsigned char buffer[PACKETBUFF+1]; #ifdef HAVE_SSL SSL_CTX *SSL_CTX_client; SSL_CTX *SSL_CTX_server; #endif int setnonblock(int fd) { int flags; #ifdef O_NONBLOCK flags = fcntl(fd, F_GETFL, 0); if(flags == -1) flags = 0; return fcntl(fd, F_SETFL, flags | O_NONBLOCK); #else flags = 1; return ioctl(fd, FIONBIO, &flags); #endif } struct cliententry *headclient; confetti *jack; struct pdcc *headpdcc; struct ldcc *headldcc; int logprint(confetti *jr, const char *format, ...) { time_t clk; struct tm *tp; const char dayweek[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; const char month[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; va_list ap; va_start(ap, format); if(jr->logfile == NULL) return 0; time(&clk); tp = localtime(&clk); if(tp != NULL) fprintf(jr->logfile, "%.3s %.3s%3d %02d:%02d:%02d %d ", dayweek[tp->tm_wday], month[tp->tm_mon], tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec, tp->tm_year + 1900); vfprintf(jr->logfile, format, ap); fflush(jr->logfile); va_end(ap); return 0; } int thestat(char *buf,int len, struct cliententry *cptr) { int p,d; char *st[2] = { "spunbackd", "SPUNBACKD", }; d=cptr->flags; for(p=0;st[0][p];p++) { if(p+1 >= len) { break; } else { buf[p]=st[ d&1 ][p]; } d=d>>1; } buf[p]='\0'; return p; } int chanlist(char *buf,int len, struct cliententry *client) { int p,c,f; char *s; struct chanentry *cptr; p=0; cptr=client->headchan; c=0; memset(buf,0,len); while(cptr) { c++; if(p>0) { buf[p++]=' '; } for(s=cptr->chan;*s;s++) { buf[p++]=*s; if(p>=len) { break; } } if(pnext; } else { c=-1; p--; buf[p--]='\0'; for(f=3;f>0;f--) { buf[p]='.'; } cptr=NULL; } } return c; } /* * experimental matching code (WD) I hope this runs faster, who knows... */ int check_match (char *mask) { if (!index (mask, '*') && !index (mask, '?')) return 0; else if (!strcmp (mask, "*")) return 1; else return 2; } /* * Note, thanx to RogerY and Redtrio for this wonderful code. Please * keep this notice intact. -- TazQ */ /* * My wildcard matching functions. I think it works well :) Seems to be */ /* * working stablely, but not too sure. */ int match (char *str, char *wld) { int i = check_match (wld); switch (i) { case 0: return strcasecmp (str, wld); break; case 1: return 0; break; } if (!(*wld) || !(*str)) return 1; while (*wld || *str) { if (tolower (*wld) == tolower (*str)) { wld++; str++; } else if (tolower (*wld) != tolower (*str) && *wld == '*') { int i = 0; char *st; st = (char *) pmalloc( sizeof(char) * strlen(wld) + 1); while (*wld == '*' && *wld) { wld++; if (!*wld) return 0; } while (*wld && *wld != '*' && *wld != '?') { st[i] = *wld; i++; wld++; } st[i] = '\0'; while (*str && strncasecmp (st, str, strlen (st))) str++; if (!strncasecmp (st, str, strlen (st))) str += strlen (st); else return 1; } else if (tolower (*wld) != tolower (*str) && *wld == '?' && *wld && *str) { wld++; str++; } else if (tolower (*wld) != tolower (*str)) return 1; } return 0; } int passwordokay (char *s, char *pass) { char *encr; char *crypt (); if(*pass == '+') { pass++; encr = crypt(s, pass); } else encr = s; if(strcmp(encr, pass) == 0) return 1; return 0; } int connokay (struct sockaddr_in *sa, confetti * jr) { struct hostent *hp; accesslist *na; hp = gethostbyaddr ((char *) &sa->sin_addr, sizeof (struct in_addr), AF_INET); logprint(jack, "Connection from %s\n", inet_ntoa (sa->sin_addr)); if (!jr->has_alist) { return 1; } for (na = jr->alist; na; na = na->next) { if (na->type == 1) { if (!match (inet_ntoa (sa->sin_addr), na->addr)) return 1; } else if (na->type == 2 && hp) { if (!match (hp->h_name, na->addr)) return 1; } } return 0; } int lsock_read(struct lsock *ls, void *buf, size_t len) { #ifdef HAVE_SSL if(ls->ssl) { int res; res = SSL_read(ls->ssl, buf, len); if(res < 0) { int SSLerror; SSLerror = SSL_get_error(ls->ssl, res); if(SSLerror == SSL_ERROR_WANT_READ || SSLerror == SSL_ERROR_WANT_WRITE) { errno = EAGAIN; ls->repmode = REPEAT_READ; return -1; } errno = 0; return -1; } return res; } #endif return recv(ls->fd, buf, len, 0); } int lsock_write(struct lsock *ls, void *buf, size_t len) { #ifdef HAVE_SSL if(ls->ssl) { int res; res = SSL_write(ls->ssl, buf, len); if(res < 0) { int SSLerror; SSLerror = SSL_get_error(ls->ssl, res); if(SSLerror == SSL_ERROR_WANT_READ || SSLerror == SSL_ERROR_WANT_WRITE) { errno = EAGAIN; ls->repmode = REPEAT_WRITE; return -1; } errno = 0; return -1; } return res; } #endif return send(ls->fd, buf, len, 0); } int send_queued(struct lsock *ls) { int res; int length; char *msg; while(sbuf_getlength(&ls->sendq) > 0) { msg = sbuf_pagemap(&ls->sendq, &length); if(msg == NULL) break; /*XXX*/ if(length <= 0) break; /*XXX*/ // res = send(ls->fd, msg, length, 0); res = lsock_write(ls, msg, length); if(res == -1) { if(errno == EINTR) continue; if(errno == EAGAIN) break; return -1; } sbuf_delete(&ls->sendq, res); } return 0; } int wipeclient(struct cliententry *cptr) { wipechans(cptr); if(cptr->loc.fd > -1) { send_queued(&cptr->loc); close(cptr->loc.fd); cptr->loc.fd=-1; } if(cptr->srv.fd > -1) { send_queued(&cptr->srv); close(cptr->srv.fd); cptr->srv.fd=-1; } #ifdef HAVE_SSL if(cptr->loc.ssl) { SSL_free(cptr->loc.ssl); cptr->loc.ssl = NULL; } if(cptr->srv.ssl) { SSL_free(cptr->srv.ssl); cptr->srv.ssl = NULL; } #endif sbuf_clear(&cptr->loc.sendq); sbuf_clear(&cptr->loc.recvq); sbuf_clear(&cptr->srv.sendq); sbuf_clear(&cptr->srv.recvq); return 0; } int freeclientlist (struct cliententry *cptr) { struct cliententry *hptr; while (cptr != NULL) { hptr = cptr->next; wipeclient(cptr); cptr = hptr; } return 1; } int bewmstick (void) { freeclientlist(headclient); headclient = NULL; bnckill(FATALITY); return 0; } struct cliententry *getclient (struct cliententry *cptr, int nfd) { while (cptr != NULL) { if (cptr->loc.fd == nfd) { return cptr; } if (cptr->srv.fd == nfd) { return cptr; } cptr = cptr->next; } return NULL; } int countfds (struct cliententry *cptr) { int p; p = 0; for(p=0;cptr;cptr=cptr->next) { if(cptr->loc.fd == -1) { continue; } p++; } return p; } char *gethost (struct in_addr *addr) { struct hostent *hp; hp = gethostbyaddr ((char *) addr, sizeof (struct in_addr), AF_INET); if(hp) { return hp->h_name; } return inet_ntoa (*addr); } int identwd_lock(struct sockaddr_in *sin, int port) { FILE *fp = NULL; struct passwd *pw; char filename[1024]; pw = getpwuid (getuid ()); if(pw == NULL) { return -1; } sprintf (filename, "%s/.identwd/%s.%i.LOCK", pw->pw_dir, inet_ntoa (sin->sin_addr), port); fp = fopen (filename, "w"); if (fp) { fprintf (fp, "WAIT\n"); fclose(fp); return 0; } return -1; } int identwd_unlock(int s, struct sockaddr_in *sin, int port, char *uname) { FILE *fp = NULL; struct passwd *pw; char filename[1024]; pw = getpwuid(getuid()); if(pw == NULL) { return -1; } sprintf (filename, "%s/.identwd/%s.%i.LOCK", pw->pw_dir, inet_ntoa (sin->sin_addr), port); fp = fopen (filename, "w"); if (fp) { int len = sizeof (struct sockaddr_in); struct sockaddr_in mysa; getsockname (s, (struct sockaddr *) &mysa, &len); fprintf (fp, (char *) "%s %i %s\n", (char *) inet_ntoa (mysa.sin_addr), (u_short) ntohs (mysa.sin_port), uname); fclose(fp); return 0; } return -1; } int irc_connect(struct cliententry *cptr, char *server, u_short port, char *pass, int ctype, int cflags) { int fd; int res; struct hostent *he; int rfamily; int lfamily; int dolocal; struct sockaddr_in lsin4; struct sockaddr_in rsin4; struct sockaddr_in6 lsin6; struct sockaddr_in6 rsin6; cptr->flags &= ~FLAGAUTOCONN; cptr->susepass=0; if(cptr->flags & FLAGCONNECTED) { tprintf(&cptr->loc, "NOTICE AUTH :Disconnecting old\n", server, port); if(cptr->srv.fd > -1) { close(cptr->srv.fd); cptr->srv.fd=-1; } cptr->flags &= ~FLAGCONNECTED; } sbuf_clear(&cptr->srv.sendq); sbuf_clear(&cptr->srv.recvq); tprintf(&cptr->loc, "NOTICE AUTH :Making reality through %s port %i\n", server, port); memset(&lsin4, 0, sizeof(lsin4)); memset(&rsin4, 0, sizeof(rsin4)); memset(&lsin6, 0, sizeof(lsin6)); memset(&rsin6, 0, sizeof(rsin6)); lfamily = rfamily = AF_INET; do { res = inet_pton(AF_INET, server, &rsin4.sin_addr); if(res) { rfamily = AF_INET; break; } res = inet_pton(AF_INET6, server, &rsin6.sin6_addr); if(res) { rfamily = AF_INET6; break; } #ifdef HAVE_GETHOSTBYNAME2 if(ctype == 1) { he = gethostbyname2(server, AF_INET6); if(he) { rfamily = AF_INET6; memcpy(&rsin6.sin6_addr, he->h_addr, he->h_length); break; } } #endif he = gethostbyname(server); if(he) { rfamily = AF_INET; memcpy(&rsin4.sin_addr, he->h_addr, he->h_length); break; } #ifdef HAVE_GETHOSTBYNAME2 if(ctype != 1) { he = gethostbyname2(server, AF_INET6); if(he) { rfamily = AF_INET6; memcpy(&rsin6.sin6_addr, he->h_addr, he->h_length); break; } } #endif return -1; } while(0); do { char *server; server = cptr->vhost; if(server == NULL) { dolocal = 0; break; } dolocal = 1; res = inet_pton(AF_INET, server, &lsin4.sin_addr); if(res) { lfamily = AF_INET; break; } res = inet_pton(AF_INET6, server, &lsin6.sin6_addr); if(res) { lfamily = AF_INET6; break; } #ifdef HAVE_GETHOSTBYNAME2 if(ctype == 1) { he = gethostbyname2(server, AF_INET6); if(he) { lfamily = AF_INET6; memcpy(&lsin6.sin6_addr, he->h_addr, he->h_length); break; } } #endif he = gethostbyname(server); if(he) { lfamily = AF_INET; memcpy(&lsin4.sin_addr, he->h_addr, he->h_length); break; } #ifdef HAVE_GETHOSTBYNAME2 if(ctype != 1) { he = gethostbyname2(server, AF_INET6); if(he) { lfamily = AF_INET6; memcpy(&lsin6.sin6_addr, he->h_addr, he->h_length); break; } } #endif lfamily = AF_INET; dolocal = 0; } while(0); if(dolocal && lfamily != rfamily) dolocal = 0; if(rfamily == AF_INET) { rsin4.sin_family = AF_INET; rsin4.sin_port = htons (port); } else /* if(rfamily == AF_INET6) */ { rsin6.sin6_family = AF_INET6; rsin6.sin6_port = htons(port); } lsin4.sin_family = AF_INET; fd = socket (rfamily, SOCK_STREAM, 0); if(fd == -1) { tprintf(&cptr->loc, "NOTICE AUTH :Failed Connection\n"); return -1; } res = setnonblock(fd); if(res == -1) { tprintf(&cptr->loc, "NOTICE AUTH :Failed Connection\n"); close(fd); return -1; } if(dolocal) { if(lfamily == AF_INET) bind (fd, (struct sockaddr *) &lsin4, sizeof(lsin4)); else /* if(lfamily == AF_INET6) */ bind(fd, (struct sockaddr *) &lsin6, sizeof(lsin6)); } if(rfamily == AF_INET && jack->identwd) identwd_lock(&rsin4, port); if(rfamily == AF_INET) res = connect(fd, (struct sockaddr *)&rsin4, sizeof(rsin4)); else /* if(rfamily == AF_INET6) */ res = connect(fd, (struct sockaddr *)&rsin6, sizeof(rsin6)); if( res == -1) { switch(errno) { case EINPROGRESS: cptr->flags |= FLAGCONNECTING; break; default: tprintf(&cptr->loc, "NOTICE AUTH :Failed Connection\n"); close(fd); return -1; } } else { tprintf(&cptr->loc, "NOTICE AUTH :Suceeded connection\n"); logprint(jack, "(%i) %s!%s@%s connected to %s\n", cptr->loc.fd, cptr->nick, cptr->uname, cptr->fromip, server); } if(rfamily == AF_INET && jack->identwd) identwd_unlock(fd, &rsin4, port, cptr->uname); strncpy (cptr->onserver, server, HOSTLEN); cptr->onserver[HOSTLEN]='\0'; strncpy (cptr->sid, server, HOSTLEN); cptr->sid[HOSTLEN]='\0'; #ifdef HAVE_SSL cptr->srv.ssl = NULL; if(cflags & USE_SSL) { cptr->srv.ssl = SSL_new(SSL_CTX_client); if(cptr->srv.ssl == NULL) { close(fd); return -1; } SSL_set_fd(cptr->srv.ssl, fd); res = SSL_connect(cptr->srv.ssl); if(res < 0) { int SSLerror; SSLerror = SSL_get_error(cptr->srv.ssl, res); if(!(SSLerror == SSL_ERROR_WANT_READ || SSLerror == SSL_ERROR_WANT_WRITE)) { SSL_free(cptr->srv.ssl); close(fd); return -1; } } } #endif cptr->srv.fd = fd; cptr->flags |= FLAGCONNECTED; if(pass) tprintf(&cptr->srv, "PASS :%s\n", pass); tprintf(&cptr->srv, "NICK %s\n", cptr->nick); tprintf(&cptr->srv, "USER %s \"%s\" \"%s\" :%s\n", cptr->uname, cptr->fromip, cptr->onserver, cptr->realname); return 0; } /* trust the caller of handleclient to filter \0 \n and \r * expects buflen be the len of the string, not counting the \0 * also expects a \0 to be appended. */ int handleclient (struct cliententry *cptr, int fromwho, int buflen, char *buf) { int f; char *prefix; char *pargv[9+1]; static char ibuf[512+1]; char *src; char *eos; int clen; int pargc; int iswhite; f=0; if(buflen <= 0) return 0; clen = buflen; if(clen > 512) clen= 512; memcpy(ibuf, buf, clen); src = ibuf; eos = src + clen; *eos = '\0'; for(;*src == ' '; src++); /* skip leading whitespace */ prefix=NULL; pargc=0; iswhite=0; if(*src == ':') { prefix=++src; pargc=0; } else pargv[pargc++] = src; for(;src < eos; src++) { if(iswhite) /* inside the whitespace */ { if( *src == ':' ) { pargv[pargc]=src + 1; pargc++; break; /* hit a string, meaning done */ } if(*src != ' ') { iswhite=0; /* no longer white */ pargv[pargc++]=src; if(pargc > 9) break; } } else { if(*src == ' ') { *src='\0'; iswhite=1; } } } f = handlepclient(cptr, fromwho, pargc, pargv, prefix); if((cptr->flags & FLAGCONNECTED) && (f == FORWARDCMD)) { f = 0; if(fromwho == CLIENT) { tprintf(&cptr->srv, "%s\n", buf); } else { /* don't forward anything if its docked */ if( !(cptr->flags & FLAGDOCKED)) { tprintf(&cptr->loc, "%s\n", buf); } } } return f; } void growfds(struct selectfds *fds, int hint) { int x; struct selectfds nset; if(hint < fds->fd_setsize) return; x = (hint + (FD_SETSIZE - 1)) / FD_SETSIZE; nset.fd_setsize = FD_SETSIZE * x; nset.size = sizeof(*nset.rfds) * x; nset.rfds = realloc(fds->rfds, nset.size); nset.wfds = realloc(fds->wfds, nset.size); nset.efds = realloc(fds->efds, nset.size); if((nset.rfds == NULL) || (nset.wfds == NULL) || (nset.efds == NULL)) bnckill(FATALITY); // not enough memory for basic functions memset( ((char *)nset.rfds) + fds->size, 0, nset.size - fds->size); memset( ((char *)nset.wfds) + fds->size, 0, nset.size - fds->size); memset( ((char *)nset.efds) + fds->size, 0, nset.size - fds->size); fds->size = nset.size; fds->fd_setsize = nset.fd_setsize; fds->rfds = nset.rfds; fds->wfds = nset.wfds; fds->efds = nset.efds; } void selectfd(struct selectfds *fds, int fd, int flags) { if(fd < 0) bnckill(FATALITY); if(fd >= fds->fd_setsize) growfds(fds, fd); // no return on fail if(flags & S_READ) FD_SET(fd, fds->rfds); if(flags & S_WRITE) FD_SET(fd, fds->wfds); if(flags & S_EXCEPT) FD_SET(fd, fds->efds); if(fd >= fds->nfds) fds->nfds = fd + 1; } void initldcc(struct selectfds *fds) { struct ldcc *dccptr; for(dccptr = headldcc; dccptr; dccptr=dccptr->next) { selectfd(fds, dccptr->fd, S_READ); } } void chkldcc(struct selectfds *fds) { int res; struct sockaddr_in sin; socklen_t sinlen; struct ldcc *dccptr; struct ldcc **parent; struct pdcc *mptr; parent = &headldcc; dccptr = headldcc; while(dccptr) { if(dccptr->fd >= 0 && dccptr->fd < fds->fd_setsize && FD_ISSET(dccptr->fd, fds->rfds)) { sinlen = sizeof(sin); res = accept(dccptr->fd, (struct sockaddr *)&sin, &sinlen); if(res == -1) { if(errno == EWOULDBLOCK) goto donext; if(errno == EINTR) continue; close(dccptr->fd); *parent = dccptr->next; free(dccptr); dccptr=*parent; continue; } mptr = malloc(sizeof(struct pdcc)); if(mptr == NULL) { close(res); goto terml; } memset(mptr, 0 , sizeof(*mptr)); mptr->lfd = res; setnonblock(mptr->lfd); mptr->rfd = socket (AF_INET, SOCK_STREAM, 0); if(mptr->rfd == -1) { close(mptr->lfd); free(mptr); goto terml; } setnonblock(mptr->rfd); rr_conn: res = connect(mptr->rfd, &dccptr->sin, dccptr->sinlen); if(res == -1) { switch(errno) { case EINTR: goto rr_conn; case EINPROGRESS: mptr->flags |= 1; break; default: perror("connect"); close(mptr->lfd); close(mptr->rfd); goto terml; } } mptr->next = headpdcc; headpdcc = mptr; terml: close(dccptr->fd); *parent = dccptr->next; free(dccptr); dccptr=*parent; continue; } donext: parent = &dccptr->next; dccptr = dccptr->next; } } void initpdcc(struct selectfds *fds) { struct pdcc *dccptr; for(dccptr = headpdcc; dccptr; dccptr=dccptr->next) { if(sbuf_getlength(&dccptr->rsendq) > 0) { selectfd(fds, dccptr->rfd, S_WRITE); } else { selectfd(fds, dccptr->lfd, S_READ); } if(sbuf_getlength(&dccptr->lsendq) > 0) { selectfd(fds, dccptr->lfd, S_WRITE); } else { selectfd(fds, dccptr->rfd, S_READ); } } } int dccsend(int fd, struct sbuf *sendq) { int res; int length; char *msg; while(sbuf_getlength(sendq) > 0) { msg = sbuf_pagemap(sendq, &length); if(msg == NULL) break; /*XXX*/ if(length <= 0) break; /*XXX*/ res = send(fd, msg, length, 0); if(res == -1) { if(errno == EINTR) continue; if(errno == EAGAIN) break; return 1; } sbuf_delete(sendq, res); } return 0; } int dccrecv(int fd, struct sbuf *recvq) { int res; for(;;) { res = recv(fd, allbuf, PACKETBUFF, 0); if(res == -1) { if(errno == EINTR) continue; if(errno == EAGAIN) break; return 1; } if(res == 0) return 2; sbuf_put(recvq, allbuf, res); } return 0; } void chkpdcc(struct selectfds *fds) { int res; struct pdcc *dccptr; struct pdcc **parent; parent = &headpdcc; dccptr = headpdcc; while(dccptr) { if(dccptr->lfd >= 0 && dccptr->lfd < fds->fd_setsize && FD_ISSET(dccptr->lfd, fds->wfds)) { res = dccsend(dccptr->lfd, &dccptr->lsendq); if(res) goto done_err; } if(dccptr->rfd >= 0 && dccptr->rfd < fds->fd_setsize && FD_ISSET(dccptr->rfd, fds->wfds)) { res = dccsend(dccptr->rfd, &dccptr->rsendq); if(res) goto done_err; } if(dccptr->lfd >= 0 && dccptr->lfd < fds->fd_setsize && FD_ISSET(dccptr->lfd, fds->rfds)) { res = dccrecv(dccptr->lfd, &dccptr->rsendq); if(res == 1) goto done_err; if(res == 2) goto done_leof; } if(dccptr->rfd >= 0 && dccptr->rfd < fds->fd_setsize && FD_ISSET(dccptr->rfd, fds->rfds)) { res = dccrecv(dccptr->rfd, &dccptr->lsendq); if(res == 1) goto done_err; if(res == 2) goto done_reof; } next_dcc: parent = &dccptr->next; dccptr = dccptr->next; continue; done_err: if(dccptr->lfd >= 0) close(dccptr->lfd); if(dccptr->rfd >= 0) close(dccptr->rfd); sbuf_clear(&dccptr->lsendq); sbuf_clear(&dccptr->rsendq); *parent = dccptr->next; free(dccptr); dccptr=*parent; continue; done_leof: dccsend(dccptr->lfd, &dccptr->lsendq); close(dccptr->lfd); dccptr->lfd = -1; if(dccptr->rfd >= 0 && sbuf_getlength(&dccptr->rsendq) > 0) goto next_dcc; goto done_err; done_reof: dccsend(dccptr->rfd, &dccptr->rsendq); close(dccptr->rfd); dccptr->rfd = -1; if(dccptr->lfd >= 0 && sbuf_getlength(&dccptr->lsendq) > 0) goto next_dcc; goto done_err; } } void initclient(struct selectfds *fds) { #ifdef HAVE_SSL int SSLerror; #endif struct cliententry *cptr; int length; for(cptr=headclient;cptr;cptr=cptr->next) { /* handle write set first */ if(cptr->loc.fd > -1) { #ifdef HAVE_SSL if(cptr->loc.ssl) { SSLerror = SSL_get_error(cptr->loc.ssl, 0); } else SSLerror = 0; if( (length = sbuf_getlength(&cptr->loc.sendq)) || SSLerror == SSL_ERROR_WANT_WRITE) #else if((length = sbuf_getlength(&cptr->loc.sendq))) #endif { if(length > HIGHOVL) { if(cptr->srv.fd >= 0) cptr->srv.flags |= FLAGDRAIN; } if(length < LOWOVL) { if(cptr->srv.fd >= 0) cptr->srv.flags &= ~FLAGDRAIN; } selectfd(fds, cptr->loc.fd, S_WRITE); } } if(cptr->flags & FLAGCONNECTED && cptr->srv.fd > -1) { if((length = sbuf_getlength(&cptr->srv.sendq))) { if(length > HIGHOVL) { if(cptr->loc.fd >= 0) cptr->loc.flags |= FLAGDRAIN; } if(length < LOWOVL) { if(cptr->loc.fd >= 0) cptr->loc.flags &= ~FLAGDRAIN; } selectfd(fds, cptr->srv.fd, S_WRITE); } } /* now set read(s) if not draining */ if(cptr->loc.fd > -1 && !(cptr->loc.flags & FLAGDRAIN)) { selectfd(fds, cptr->loc.fd, S_READ); } if(cptr->flags & FLAGCONNECTED && cptr->srv.fd > -1 && !(cptr->srv.flags & FLAGDRAIN) ) { selectfd(fds, cptr->srv.fd, S_READ); } } } char mline[512]; int scanclient (struct cliententry *cptr, struct selectfds *fds) { int f; int res; if(cptr->loc.fd >= 0 && cptr->loc.fd < fds->fd_setsize && FD_ISSET(cptr->loc.fd, fds->rfds)) { #ifdef HAVE_SSL if(cptr->loc.repmode == REPEAT_ACCEPT) { res = SSL_accept(cptr->loc.ssl); logprint(jack, "SSL_accept %d\n", res); if(res <= 0) { int SSLerror; SSLerror = SSL_get_error(cptr->loc.ssl, res); logprint(jack, "SSL_get_error %d %d\n", res, SSLerror); if(!(SSLerror == SSL_ERROR_WANT_READ || SSLerror == SSL_ERROR_WANT_WRITE)) { close(cptr->loc.fd); cptr->loc.fd = -1; return KILLCURRENTUSER; } } } #endif res = lsock_read(&cptr->loc, allbuf, PACKETBUFF); if(res == -1) { if(errno == EINTR || errno == EAGAIN) goto cr_out; if(cptr->flags & FLAGDOCKED) { if(cptr->srv.fd == -1) { close(cptr->loc.fd); cptr->loc.fd = -1; return KILLCURRENTUSER; } close(cptr->loc.fd); cptr->loc.fd=DOCKEDFD; return 0; } else return KILLCURRENTUSER; } if(res == 0) { if(cptr->flags & FLAGDOCKED) { if(cptr->srv.fd == -1) { close(cptr->loc.fd); cptr->loc.fd = -1; return KILLCURRENTUSER; } close(cptr->loc.fd); cptr->loc.fd=DOCKEDFD; return 0; } else return KILLCURRENTUSER; } sbuf_put(&cptr->loc.recvq, allbuf, res); for(;;) { res = sbuf_getmsg(&cptr->loc.recvq, mline, 512); if(res <= 0) break; f = handleclient(cptr, CLIENT, res - 1, mline); if(f) return f; } } cr_out: if(cptr->srv.fd >= 0 && cptr->srv.fd < fds->fd_setsize && FD_ISSET(cptr->srv.fd, fds->rfds)) { res = lsock_read(&cptr->srv, allbuf, PACKETBUFF); if(res == -1) { if(errno == EINTR || errno == EAGAIN) goto sr_out; if(cptr->flags & FLAGKEEPALIVE) return SERVERDIED; else return KILLCURRENTUSER; } if(res == 0) { if(cptr->flags & FLAGKEEPALIVE) return SERVERDIED; else return KILLCURRENTUSER; } sbuf_put(&cptr->srv.recvq, allbuf, res); for(;;) { res = sbuf_getmsg(&cptr->srv.recvq, mline, 512); if(res <= 0) break; f = handleclient(cptr, SERVER, res - 1, mline); if(f) return f; } } sr_out: return 0; } void chkclient(struct selectfds *fds) { int p; struct cliententry *cptr; for(cptr=headclient;cptr;cptr=cptr->next) { p=scanclient(cptr,fds); if(p) goto han_err; if(cptr->loc.fd >= 0 && cptr->loc.fd < fds->fd_setsize && FD_ISSET(cptr->loc.fd, fds->wfds)) { p = send_queued(&cptr->loc); if(p == -1) { p = KILLCURRENTUSER; goto han_err; } } if(cptr->srv.fd >= 0 && cptr->srv.fd < fds->fd_setsize && FD_ISSET(cptr->srv.fd, fds->wfds)) { p = send_queued(&cptr->srv); if(p == -1) { p = SERVERDIED; goto han_err; } if(cptr->flags & FLAGCONNECTING) { cptr->flags &= ~FLAGCONNECTING; tprintf(&cptr->loc, "NOTICE AUTH :Suceeded connection\n"); logprint(jack, "(%i) %s!%s@%s connected to %s\n", cptr->loc.fd, cptr->nick, cptr->uname, cptr->fromip, cptr->onserver); } } han_err: if(p == KILLCURRENTUSER) /* self death */ { wipeclient(cptr); return; } if(p == SERVERDIED) /* keep alive */ { // page_free(&cptr->page_server); sbuf_clear(&cptr->srv.sendq); sbuf_clear(&cptr->srv.recvq); tprintf(&cptr->loc, "NOTICE AUTH :IRC quit, KeepAlive here.\n", cptr->nick); cptr->flags &= ~FLAGCONNECTING; if(cptr->flags & FLAGCONNECTED) { if( cptr->srv.fd != -1) { close(cptr->srv.fd); cptr->srv.fd=-1; } cptr->flags &= ~FLAGCONNECTED; } } } return; } int addon_client(int citizen, struct sockaddr_in *nin) { int res; int ninlen; struct in_addr faddr; struct cliententry *cptr; setnonblock(citizen); if (jack->maxusers) { if (countfds (headclient) + 1 > jack->maxusers) { return -1; } } ninlen = sizeof(struct sockaddr_in); res=getpeername (citizen, (struct sockaddr *)nin, &ninlen); if(res) { return -1; } if (!connokay (nin, jack)) { return -1; } for(cptr=headclient;cptr;cptr=cptr->next) { if((cptr->loc.fd == -1) && (cptr->srv.fd == -1)) break; } if(cptr == NULL) { cptr = (struct cliententry *) pmalloc( sizeof( struct cliententry )); if( cptr == NULL) { return -1; } memset(cptr, 0, sizeof(struct cliententry)); cptr->prev=NULL; cptr->next=headclient; if( headclient != NULL) { headclient->prev=cptr; } headclient=cptr; } cptr->loc.fd = citizen; cptr->srv.fd = -1; cptr->flags=0; cptr->headchan=NULL; strncpy(cptr->vhost, jack->vhostdefault, HOSTLEN); cptr->vhost[HOSTLEN]='\0'; strcpy(cptr->nick,"UNKNOWN"); faddr = nin->sin_addr; strncpy (cptr->fromip, gethost (&faddr), HOSTLEN); cptr->fromip[HOSTLEN]='\0'; if (jack->dpassf == 0) { cptr->flags |= FLAGPASS; } sbuf_claim(&cptr->loc.recvq); sbuf_claim(&cptr->loc.sendq); sbuf_claim(&cptr->srv.recvq); sbuf_claim(&cptr->srv.sendq); // cptr->blen=0; #ifdef USE_SSL cptr->srv.ssl = NULL; cptr->loc.ssl = NULL; if(jack->optflags & USE_SSL) { cptr->loc.ssl = SSL_new(SSL_CTX_server); if(cptr->loc.ssl == NULL) { wipeclient(cptr); return 0; } SSL_set_fd(cptr->loc.ssl, citizen); res = SSL_accept(cptr->loc.ssl); logprint(jack, "SSL_accept %d\n", res); if(res <= 0) { int SSLerror; SSLerror = SSL_get_error(cptr->loc.ssl, res); logprint(jack, "SSL_get_error %d %d\n", res, SSLerror); if(!(SSLerror == SSL_ERROR_WANT_READ || SSLerror == SSL_ERROR_WANT_WRITE)) { SSL_free(cptr->srv.ssl); wipeclient(cptr); return 0; } } } #endif return 0; } int initproxy (confetti * jr) { int opt; int res; struct sockaddr *sin; socklen_t sinlen; struct sockaddr_in sin4; struct sockaddr_in6 sin6; struct hostent *he; #if HAVE_SSL SSL_library_init(); SSLeay_add_ssl_algorithms(); SSL_load_error_strings(); ERR_load_crypto_strings(); SSL_CTX_client = SSL_CTX_new(SSLv23_client_method()); if(SSL_CTX_client == NULL) { logprint(jr, "Failed to create client context\n"); return FATALITY; } SSL_CTX_server = SSL_CTX_new(SSLv23_server_method()); if(SSL_CTX_client == NULL) { logprint(jr, "Failed to create server context\n"); return FATALITY; } if(*jr->public_cert_file) { res = SSL_CTX_use_certificate_file(SSL_CTX_server, jr->public_cert_file, SSL_FILETYPE_PEM); if(res <= 0) { logprint(jr, "Failed to initilize SSL Certificate File \"%s\"\n", jr->public_cert_file); return PUBLICCERTERR; } } if(*jr->private_cert_file) { res = SSL_CTX_use_PrivateKey_file(SSL_CTX_server, jr->private_cert_file, SSL_FILETYPE_PEM); if(res <= 0) { logprint(jr, "Failed to use Private Certificate\n"); return PRIVATECERTERR; } res = SSL_CTX_check_private_key(SSL_CTX_server); if(res == 0) { logprint(jr, "Server certificate does not match Server key\n"); return PRIVATECERTERR; } } #endif memset(&sin4, 0, sizeof(sin4)); sin4.sin_family = AF_INET; sin4.sin_port = htons(jr->dport); sin4.sin_addr.s_addr = INADDR_ANY; memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(jr->dport); sin6.sin6_addr = in6addr_any; sin = (struct sockaddr *)&sin4; sinlen = sizeof(sin4); if(*jr->dhost) { res = inet_pton(AF_INET, jr->dhost, &sin4.sin_addr); if(res == 0) { res = inet_pton(AF_INET6, jr->dhost, &sin6.sin6_addr); if(res == 1) { sin = (struct sockaddr *)&sin6; sinlen = sizeof(sin6); } else if(res == 0) { he = gethostbyname(jr->dhost); if (he) { memcpy (&sin4.sin_addr, he->h_addr, he->h_length); } } } } s_sock = socket(sin->sa_family, SOCK_STREAM, 0); if(s_sock < 0) { return SOCKERR; } opt = 1; setsockopt (s_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)); res = bind(s_sock, sin, sinlen); if(res == -1) { close (s_sock); return BINDERR; } if (listen (s_sock, 10) < 0) { return LISTENERR; } return 0; } int ircproxy (confetti * jr) { int res; int r,nfd; struct sockaddr_in nin; socklen_t ninlen; struct selectfds set; jack = jr; memset(&set, 0, sizeof(set)); set.nfds = 0; set.fd_setsize = 0; set.rfds = NULL; set.wfds = NULL; set.efds = NULL; growfds(&set, getdtablesize()); for(;;) { set.nfds = 0; memset(set.rfds, 0, set.size); memset(set.wfds, 0, set.size); memset(set.efds, 0, set.size); initclient(&set); initpdcc(&set); initldcc(&set); selectfd(&set, s_sock, S_READ); res = select(set.nfds, set.rfds, set.wfds, set.efds, NULL); if(res == -1) { if(errno == ENOMEM) { bnckill (SELECTERR); } bnckill(FATALITY); } if(s_sock >= 0 && s_sock < set.fd_setsize && FD_ISSET(s_sock, set.rfds)) { ninlen = sizeof(nin); nfd = accept (s_sock, (struct sockaddr *) &nin, &ninlen); if(nfd != -1) { r=addon_client(nfd,&nin); if(r == -1) { close(nfd); } } } chkldcc(&set); chkpdcc(&set); chkclient(&set); } return 0; }