Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Class Members | File Members | Related Pages

server.c

Go to the documentation of this file.
00001 /* $Id: server.c,v 1.2 2003/07/31 07:41:44 Mysid Exp $ */
00002 
00003 /*
00004  * Copyright (c) 1996-1997 Chip Norkus
00005  * Copyright (c) 1997 Max Byrd
00006  * Copyright (c) 1997 Greg Poma
00007  * Copyright (c) 1999 Portions Copyright
00008  * All rights reserved.
00009  *
00010  * Redistribution and use in source and binary forms, with or without
00011  * modification, are permitted provided that the following conditions
00012  * are met:
00013  * 1. Redistributions of source code must retain the above copyright
00014  *    notice, this list of conditions and the following disclaimer.
00015  * 2. Redistributions in binary form must reproduce the above copyright
00016  *    notice, this list of conditions and the following disclaimer in the
00017  *    documentation and/or other materials provided with the distribution.
00018  * 3. Neither the name of the authors nor the names of its contributors
00019  *    may be used to endorse or promote products derived from this software
00020  *    without specific prior written permission.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  */
00034 
00041 #include "services.h"
00042 #include "nickserv.h"
00043 #include "operserv.h"
00044 #include "memoserv.h"
00045 #include "chanserv.h"
00046 #include "infoserv.h"
00047 #include "gameserv.h"
00048 #include "mass.h"
00049 #include "log.h"
00050 
00051 void parseLine(char *);
00052 
00059 int
00060 ConnectToServer(char *hostname, int portnum)
00061 {
00062     fd_set wfd, xfd;
00063     struct sockaddr_in sa;
00064     struct hostent *hp;
00065     int s;
00066 
00067     if ((hp = gethostbyname(hostname)) == NULL) {
00068         errno = ECONNREFUSED;
00069         return (-1);
00070     }
00071 
00072     bzero(&sa, sizeof(sa));
00073     bcopy(hp->h_addr, (char *)&sa.sin_addr, hp->h_length);  /* set address */
00074     sa.sin_family = hp->h_addrtype;
00075     sa.sin_port = htons((u_short) portnum);
00076 
00077     if ((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0)   /* get socket */
00078         return (-1);
00079     if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) {    /* connect */
00080         printf("Error:  %s\n", strerror(errno));
00081         close(s);
00082         return (-1);
00083     }
00084 
00085     /*
00086      * whoo hoo, we got connected.  For some reason, if we don't pause here a
00087      * little while, we loose. XXX This should be fixed...
00088      */
00089     FD_ZERO(&wfd);
00090     FD_ZERO(&xfd);
00091     FD_SET(s, &wfd);
00092     FD_SET(s, &xfd);
00093     if (select(s + 2, NULL, &wfd, &xfd, NULL) < 0 || !FD_ISSET(s, &wfd)
00094         || FD_ISSET(s, &xfd)) {
00095         logDump(corelog,
00096                 "Error occured while attempting to establish connection [%d]",
00097                 errno);
00098         flushLogs(NULL);
00099         sshutdown(2);
00100     }
00101 
00102     return (s);
00103 }
00104 
00111 void
00112 sSend(char *format, ...)
00113 {
00114     char sBuffer[IRCBUF];
00115     va_list stuff;
00116 
00117     /*
00118      * set things up to print into the buffer.  Up to sizeof(sBuffer) - 2 is
00119      * used (well, -3, since vsnprintf saves room for the \0 as well) and
00120      * the \r\n is appended using strcat.
00121      */
00122     va_start(stuff, format);
00123     vsnprintf(sBuffer, sizeof(sBuffer) - 3, format, stuff);
00124     va_end(stuff);
00125 
00126     /*
00127      * Append a \r\n to the buffer, and send it to the server
00128      */
00129     strcat(sBuffer, "\r\n");    /* strcat is safe */
00130 #if 0
00131     printf("Writing: %s\n", sBuffer);
00132 #endif
00133     if (net_write(server, sBuffer, strlen(sBuffer)) == -1) {
00134         if (errno == EPIPE || errno == EBADF || errno == EINVAL ||
00135                     errno == EFAULT) {
00136                     logDump(corelog, "Terminating on write error, errno=%d", errno);
00137                     sshutdown(0);
00138                 }
00139     }
00140 
00141 #ifdef DEBUG
00142     printf("-> %s\n", sBuffer);
00143 #endif
00144 }
00145 
00152 void
00153 addUser(char *nick, char *user, char *host, char *name, char *mode)
00154 {
00155     if (!nick || !*nick) {
00156         return;
00157     }
00158     sSend("NICK %s 1 0 %s %s %s :%s", nick, user, host, myname, name);
00159     sSend(":%s MODE %s :%s", nick, nick, mode);
00160 }
00161 
00162 
00163 /* Breakline code starts here */
00164 
00166 #define MAX_IRC_LINE_LEN 512
00167 
00173 char oldData[MAX_IRC_LINE_LEN];
00174 
00181 void
00182 breakLine(char *tmpbuffer)
00183 {
00184     int usedbuf, terminated;
00185     char sendBuffer[MAX_IRC_LINE_LEN + 1];
00186 
00187     memset(sendBuffer, 0, MAX_IRC_LINE_LEN + 1);
00188 
00189     usedbuf = 0;
00190 
00191     /* Just in case the impossible happens, and our old data is >= 512 chars,
00192      * which means we won't be able to add anything from the data stream, and
00193      * therefore can't get the terminating \r\n
00194      */
00195     if (strlen(oldData) >= MAX_IRC_LINE_LEN) {
00196         sSend(":%s GLOBOPS :Infinate loop encounted in breakLine().  "
00197               "Mail this line to coders@sorcery.net", OperServ);
00198         sSend(":%s GLOBOPS :Services terminating", OperServ);
00199         sshutdown(1);
00200     }
00201 
00202     /* If we have some leftover unterminated data from the stream... */
00203     if (oldData[0] != '\0') {
00204         strncpyzt(sendBuffer, oldData, MAX_IRC_LINE_LEN);
00205         bzero(oldData, MAX_IRC_LINE_LEN);
00206         usedbuf = strlen(sendBuffer);
00207     }
00208 
00209     while (*tmpbuffer) {
00210         terminated = FALSE;
00211 
00212         /* While each of the following are true:
00213          * @ We've not found a terminator
00214          * @ We're not at the end of the buffer
00215          * @ We're not at the end of the buffer we can copy to
00216          */
00217         while (*tmpbuffer != '\n' && *tmpbuffer != '\r'
00218                && *tmpbuffer != '\0' && usedbuf < MAX_IRC_LINE_LEN) {
00219             sendBuffer[usedbuf] = *tmpbuffer;
00220             usedbuf++;
00221             tmpbuffer++;
00222         }
00223 
00224         sendBuffer[usedbuf] = 0;
00225         usedbuf = 0;
00226 
00227         /* Clear trailing terminator */
00228         while (*tmpbuffer == '\r' || *tmpbuffer == '\n') {
00229             tmpbuffer++;
00230             terminated = TRUE;
00231         }
00232 
00233         if (terminated == FALSE) {  /* If we didn't find the terminators */
00234             /*
00235              * Copy our current unfinished line to a tmp buffer, we'll look
00236              * at it again in the next call to breakLine(), when we recieve
00237              * more data.  Had to be done due to recv() taking unteminated
00238              * data.
00239              */
00240             strncpyzt(oldData, sendBuffer, MAX_IRC_LINE_LEN);
00241         } else {
00242             /* If we've got all our data, and it's terminated, we can parse it */
00243             parseLine(sendBuffer);
00244         }
00245     }
00246 }
00247 
00257 /*
00258  * WARNING WARNING WARNING
00259  *
00260  *  This function is very ugly in terms of args handling, and it is
00261  *  very easy to inadvertently break something if you make small tweaks
00262  *  to it.
00263  *
00264  *  args and numargs do NOT always map together in this function,
00265  *  for example numargs = 3, means that args really has 3+3=6 elements
00266  *  in some points: numargs should be numargs2.
00267  *
00268  *  Basically, the bottom two elements of args[] are dropped
00269  * in this function when
00270  * it goes to args2 to call the particular handlers -Mysid
00271  */
00272 void
00273 parseLine(char *line)
00274 {
00275     int i = 0, a = 0, x = 0, prefixed = 0;
00276     char *args2[MAX_IRC_LINE_LEN + 5];
00277     char *args[MAX_IRC_LINE_LEN + 5];
00278     char realargs[151][151];
00279     u_int16_t numargs = 0;
00280     /* 
00281      * Seems Ok to me ^^^ 
00282      * sizes may be off(?)
00283      */
00284 
00285     /* Yes, your sizes were off.. -Mysid */
00286 
00287     strncpyzt(coreBuffer, line, MAX_IRC_LINE_LEN);
00288 #ifdef DEBUG
00289     printf("Read: %s\n", coreBuffer);
00290 #endif
00291 
00292     CTime = time(NULL);
00293 
00294     while (*line && x < 150) {
00295         while (*line != ' ' && *line && a < 150) {
00296             realargs[x][a] = *line;
00297             a++;
00298             line++;
00299         }
00300         realargs[x][a] = 0;
00301         args[x] = realargs[x];
00302         x++;
00303         numargs++;
00304         while (*line == ' ')
00305             line++;
00306         a = 0;
00307     }
00308 
00309     /* ensure the next item is null so we can check it later */
00310     realargs[x][0] = 0;
00311     args[x] = realargs[x];
00312 
00313     if (args[0][0] == ':') {
00314         prefixed = 1;
00318         args[0]++;
00319     } else
00320         prefixed = 0;
00321 
00322     if (!strcmp(args[0], "PING") && !prefixed) {
00323         sSend("PONG :%s", myname);
00324         return;
00325     }
00326 
00327     else if (!strcmp(args[0], "ERROR") && !strcmp(args[1], ":Closing"))
00328         sshutdown(0);
00329 
00330     else if (!strcmp(args[0], "NICK") && !prefixed) {
00331         if (strchr(args[4], '*') || strchr(args[4], '?')
00332             || strchr(args[4], '!') || strchr(args[4], '@')) {
00333             char nick[NICKLEN];
00334 
00335             strncpyzt(nick, args[1], NICKLEN);
00336             sSend
00337                 (":%s KILL %s :%s!%s (Your ident reply contains either a *, !, @, or ?. Please remove this before returning.)",
00338                  services[1].name, nick, services[1].host,
00339                  services[1].name);
00340             addGhost(nick);
00341             timer(15, delTimedGhost, strdup(nick));
00342             return;
00343         }
00344 
00345         addNewUser(args, numargs);  /* nickserv.c, add new user. */
00346         return;
00347     }
00348 
00349 
00350     if (!strcmp(args[1], "PRIVMSG")) {
00351         UserList *tmp = getNickData(args[0]);
00352 
00353 
00354         if (strchr(args[2], '#') || strchr(args[2], '$'))
00355             return;
00356 
00357         if (!strcasecmp(args[0], NickServ)
00358             || !strcasecmp(args[0], GameServ)
00359             || !strcasecmp(args[0], OperServ)
00360             || !strcasecmp(args[0], ChanServ)
00361             || !strcasecmp(args[0], MemoServ)
00362             || !strcasecmp(args[0], InfoServ)) return;
00363 
00364         if (tmp && tmp->reg && tmp->reg->flags & NBANISH) {
00365             sSend(":%s NOTICE %s :This nickname is banished."
00366                   "  You cannot use services until you change"
00367                   " nicknames.",
00368                   NickServ, args[0]);
00369             return;
00370         }
00371 
00372         if (!tmp) {
00373             nDesynch(args[0], "PRIVMSG");
00374             return;
00375         }
00376 
00377         if (addFlood(tmp, 1))
00378             return;
00379 
00380         if (isIgnored(tmp->nick, tmp->user, tmp->host)) {
00381             if (tmp->floodlevel.GetLev() < 2)
00382                 sSend
00383                     (":%s NOTICE %s :You are on services ignore, you may not use any Service",
00384                      NickServ, tmp->nick);
00385             if (!isOper(tmp) || tmp->caccess < 2 || !tmp->reg
00386                 || !(tmp->reg->opflags & OROOT))
00387                 return;
00388         }
00389 
00390         args[3]++;
00391         while (*args[3] == ' ')
00392             args[3]++;
00393 
00394         for (i = 3; i < numargs; i++)
00395             args2[i - 3] = args[i];
00396         numargs -= 3;
00397 
00398         /* Handle pings before even going to the services */
00399         if (!strcasecmp(args2[0], "\001PING")) {
00400             if (addFlood(tmp, 3))
00401                 return;
00402             if (numargs < 3)
00403                 sSend(":%s NOTICE %s :\001PING %s", args[2], args[0],
00404                       args2[1]);
00405             else
00406                 sSend(":%s NOTICE %s :\001PING %s %s", args[2], args[0],
00407                       args2[1], args2[2]);
00408             return;
00409         }
00410 
00411         /* NOTE: numargs maps to args2 not args at this point */
00412         if (!strncasecmp(args[2], OperServ, strlen(OperServ)))
00413             sendToOperServ(tmp, args2, numargs);
00414         else if (!strncasecmp(args[2], NickServ, strlen(NickServ)))
00415             sendToNickServ(tmp, args2, numargs);
00416         else if (!strncasecmp(args[2], ChanServ, strlen(ChanServ)))
00417             sendToChanServ(tmp, args2, numargs);
00418         else if (!strncasecmp(args[2], MemoServ, strlen(MemoServ)))
00419             sendToMemoServ(tmp, args2, numargs);
00420         else if (!strncasecmp(args[2], InfoServ, strlen(InfoServ)))
00421             sendToInfoServ(tmp, args2, numargs);
00422         else if (!strncasecmp(args[2], GameServ, strlen(GameServ)))
00423             sendToGameServ(tmp, args2, numargs);
00424         else if (isGhost(args[2])) {
00425             sSend
00426                 (":%s NOTICE %s :This is a NickServ registered nick enforcer, and not a real user.",
00427                  args[2], args[0]);
00428         }
00429         /* Note, the below should be correct. */
00430         else if ((numargs >= 1) && adCheck(tmp, args[2], args2, numargs))
00431             return;
00432         return;
00433     }
00434 
00435     else if (!strcmp(args[1], "QUIT")) {
00436         remUser(args[0], 0);
00437         return;
00438     } else if (!strcmp(args[1], "NICK")) {
00439         UserList *tmp = getNickData(args[0]);
00440         if (addFlood(tmp, 5))
00441             return;
00442         changeNick(args[0], args[2], args[3]);
00443         return;
00444     } else if (!strcmp(args[1], "MODE") && !strcmp(args[0], args[2])) {
00445         setMode(args[0], args[3]);
00446         return;
00447     } else if (!strcmp(args[1], "MODE")) {
00448         setChanMode(args, numargs);
00449         return;
00450     } else if (!strcmp(args[1], "TOPIC")) {
00451         setChanTopic(args, numargs);
00452         return;
00453     } else if (!strcmp(args[1], "AWAY")) {
00454         if (numargs < 3) {
00455             setFlags(args[0], NISAWAY, '-');
00456             checkMemos(getNickData(args[0]));
00457         } else
00458             setFlags(args[0], NISAWAY, '+');
00459         return;
00460     }
00461 
00462     else if (!strcmp(args[1], "JOIN")) {
00463         addUserToChan(getNickData(args[0]), args[2]);
00464         return;
00465     } else if (!strcmp(args[1], "PART")) {
00466         remUserFromChan(getNickData(args[0]), args[2]);
00467         return;
00468     } else if (!strcmp(args[1], "KICK")) {
00469         remUserFromChan(getNickData(args[3]), args[2]);
00470         return;
00471     }
00472 
00473     else if (!strcmp(args[1], "KILL")) {
00474         int i;
00475         for (i = 0; i < NUMSERVS; i++) {
00476             if (!strcasecmp(args[2], services[i].name)) {
00477                 addUser(services[i].name, services[i].uname,
00478                         services[i].host, services[i].rname,
00479                         services[i].mode);
00480                 sSend(":%s KILL %s :%s!%s (services kill protection)",
00481                       services[i].name, args[0], services[i].host,
00482                       services[i].name);
00483                 sSend(":%s GLOBOPS :%s just killed me!", services[i].name,
00484                       args[0]);
00485                 remUser(args[0], 0);
00486                 return;
00487             }
00488         }
00489 
00490         if (isGhost(args[2])) {
00491             delGhost(args[2]);
00492             return;
00493         }
00494 
00495         else
00496             remUser(args[2], 1);
00497         return;
00498     } else if (!strcmp(args[1], "MOTD")) {
00499         UserList *tmp = getNickData(args[0]);
00500         if (addFlood(tmp, 1))
00501             return;
00502         motd(args[0]);
00503         return;
00504     }
00505 
00506     else if (!strcmp(args[1], "INFO")) {
00507         UserList *tmp = getNickData(args[0]);
00508         if (!tmp || addFlood(tmp, 3))
00509             return;
00510         sendInfoReply(tmp);
00511         return;
00512     }
00513 
00514     else if (!strcmp(args[1], "VERSION")) {
00515         UserList *tmp = getNickData(args[0]);
00516         if (addFlood(tmp, 1))
00517             return;
00518         sSend(":%s 351 %s %s %s :%s", myname, args[0], VERSION_STRING,
00519               myname, VERSION_QUOTE);
00520         return;
00521     } else if ((!strcmp(args[1], "GNOTICE") || !strcmp(args[1], "GLOBOPS"))
00522                && !strcmp(args[2], ":Link") && !strcmp(args[3], "with")
00523                && !strncmp(args[4], myname, strlen(myname))) {
00524         sSend(":%s GNOTICE :Link with %s[services@%s] established.",
00525               myname, args[0], hostname);
00526         strncpyzt(hostname, args[0], sizeof(hostname));
00527 
00528         expireNicks(NULL);
00529         expireChans(NULL);
00530         sync_cfg("1");
00531         checkTusers(NULL);
00532         flushLogs(NULL);
00533         nextNsync = (SYNCTIME + CTime);
00534         nextCsync = ((SYNCTIME * 2) + CTime);
00535         nextMsync = ((SYNCTIME * 3) + CTime);
00536         loadakills();
00537         return;
00538     }
00539 #ifdef IRCD_HURTSET
00540     else if (!strcmp(args[1], "HURTSET") && (numargs >= 4)) {
00541         UserList *hurtwho;
00542         if ((hurtwho = getNickData(args[2]))) {
00543             if (args[3] && *args[3] == '-')
00544                 hurtwho->oflags &= ~(NISAHURT);
00545             else if (args[3] && atoi(args[3]) == 4) {
00546                 hurtwho->oflags |= (NISAHURT);
00547             }
00548             else if (args[3] && isdigit(*args[3])
00549                      && isAHurt(hurtwho->nick, hurtwho->user,
00550                                 hurtwho->host)) hurtwho->oflags |=
00551                     (NISAHURT);
00552         }
00553     }
00554 #endif
00555     else if (!strcmp(args[1], "SQUIT")) {
00556         time_t jupe;
00557         jupe = time(NULL);
00558         if (strchr(args[2], '.'))
00559             return;
00560         sSend(":%s WALLOPS :%s Un-jupitered by %s at %s", myname, args[2],
00561               args[0], ctime(&(jupe)));
00562         return;
00563     }
00564 
00565     else if (!strcmp(args[1], "STATS") && numargs > 3) {
00566         const char* from = args[0];
00567 
00568         if (args[2] && !strcasecmp(args[2], "OPTS"))
00569         {
00570             sSend(":%s NOTICE %s :Network name: %s", InfoServ, from, NETWORK);
00571 
00572 
00573 #ifdef AKILLMAILTO
00574                 sSend(":%s NOTICE %s :Akill log address: %s", InfoServ, from,
00575                         AKILLMAILTO);
00576 #endif
00577 
00578 #ifdef ENABLE_GRPOPS
00579                 sSend(":%s NOTICE %s :GRPops enabled.",  InfoServ, from);
00580 #endif
00581 
00582 #ifdef MD5_AUTH
00583                 sSend(":%s NOTICE %s :MD5 authentication available.",  InfoServ, from);
00584 #endif
00585 
00586         }
00587         else if (args[2] && !strcasecmp(args[2], "V$"))
00588         {
00589             sSend(":%s NOTICE %s :Based on sn services1.4.", InfoServ, from);
00590         }
00591     }
00592 
00593     /* "No N-line" error */
00594     else if (!strcmp(args[0], "ERROR") && !strcmp(args[1], ":No")) {
00595         fprintf(stderr, "Error connecting to server: No N-line\n");
00596         sshutdown(2);
00597     }
00598 }
00599 
00604 void
00605 sendInfoReply(UserList * nick)
00606 {
00607     char *from = nick->nick;
00608     extern char *services_info[];
00609     int i = 0;
00610 
00611     sSend(":%s 373 %s :Server INFO", myname, from);
00612     for (i = 0; services_info[i]; i++)
00613         sSend(":%s 371 %s :%s", myname, from, services_info[i]);
00614     sSend(":%s 374 %s :End of /INFO list.", myname, from);
00615     return;
00616 }

Generated at Sat Oct 25 20:56:09 2003 for Services using Doxygen.
Services Copyr. 1996-2001 Chip Norkus, Max Byrd, Greg Poma, Michael Graff, James Hess, Dafydd James. All rights reserved See LICENSE for licensing information.