/* * main.c * (C) Peter Salanki 2002 * This program is copyright, and covered by the Gnu Public License. * The Natasha Trojan Scan bot. * sorcer@linux.se */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "servtab.h" #include "settings.h" #include "globals.h" /* * FD of IRC server - handy to keep global. I might eventually restrict it * to only the bits that really need it, but I hardly see the point, frankly. */ int serverlink = 0; /* * globals used to pass arguments between functions - much simpler and a bit * faster than doing it "properly". */ char args[MAX_ARGS][512]; int argcount; int cchannel = 0; int maxchannel = 0; int timenext = 0; int relink = 0; int lastmysql; char sender[512]; char command[512]; char username[USERLEN]; char host[HOSTLEN]; MYSQL mysql, *sock; MYSQL_RES *res; MYSQL_ROW row; /* * OK, Useful functions like those used to join a channel, part a channel, say * something, do something, send a CTCP or a CTCP reply, and hopefully DCC * utilities eventually go here. This code just handles sending commands to * the server, and has absolutely nothing to do with parsing the replies. */ char *STR_replace_c (char *source, char *old, char *new, char *dest) { int i=0, j=0; int lold, lnew, lsource; int tempwork=0; int templen=1024; char *tempdest; /* create a temp work area if source = dest */ if (source == dest) { tempwork = 1; tempdest = calloc(templen, sizeof(char)); } else { /* since dest is different from source, do work in dest */ tempdest = dest; } lold = strlen(old); lnew = strlen(new); lsource = strlen (source); while (i < (lsource - lold + 1)) { /* if temparea used and too small, increase its size */ if (tempwork && ((j + lnew) >= templen)) { templen += 256; tempdest = realloc(tempdest, templen * sizeof(char)); } if (strncmp (&source[i], old, lold)) { tempdest[j++] = source[i++]; } else { strcpy (&tempdest[j], new); j = j + lnew; i = i + lold; } } /* if temp work area used and too small, increase its size */ if (tempwork && ((j + strlen(&source[i])) >= templen)) { templen += 256; tempdest = realloc(tempdest, templen * sizeof(char)); } tempdest[j] = 0; strcat (&tempdest[j], &source[i]); /* if a temp work area was used, copy to final dest */ if (tempwork && ((j + strlen(&source[i])) >= templen)) { templen += 256; tempdest = realloc(tempdest, templen * sizeof(char)); } tempdest[j] = 0; strcat (&tempdest[j], &source[i]); /* if a temp work area was used, copy to final dest */ if (tempwork) { strcpy(dest, tempdest); free(tempdest); } return (dest); } void dbconnect (void) { if (!(sock = mysql_real_connect(&mysql,MYSQL_SERVER,MYSQL_USER,MYSQL_PASS,MYSQL_DB,MYSQL_PORT,NULL,0))) { fprintf(stderr,"Couldn't connect to MySQL server!\n%s\n\n", mysql_error(&mysql)); perror(""); exit(1); } lastmysql = time(NULL); } void dbquery (char *query) { char tmp[512]= ""; if(mysql_query(sock, query)) { #ifdef DEBUG printf("Query failed (%s)\n", mysql_error(sock)); #endif sprintf(tmp, "%c4Warning:%c Query failed (%s). Query: %s", COLORS, COLORS, mysql_error(sock), query); privmsg(HOMECHAN, tmp); if(strcmp(mysql_error(sock), "MySQL server has gone away") == 0 || strcmp(mysql_error(sock), "Lost connection to MySQL server during query") == 0) { dbconnect(); usleep(200); mysql_query(sock, query); if(!mysql_query(sock, query)) res = mysql_store_result(sock); } } else res = mysql_store_result(sock); lastmysql = time(NULL); } char *escapequery (char *query) { char *escaped = malloc(2*strlen(query)); mysql_real_escape_string(&mysql, escaped, query, strlen(query)); return escaped; } int regex_match(char *regex, char *string) { pcre *re; const char *error; int erroffset; int rc; /* Compile the regular expression in the first argument */ re = pcre_compile(regex, 0, &error, &erroffset, NULL); /* Compilation failed: print the error message and exit */ if (re == NULL) { printf("PCRE compilation failed at offset %d: %s\n", erroffset, error); return 0; } /* Compilation succeeded: match the subject in the second argument */ rc = pcre_exec(re, NULL, string, (int)strlen(string), 0, 0, NULL, 0); if (rc < 0) return 0; return 1; } void puttime () { char buffer[32]; sprintf (buffer, " %ld ", (long) time (0)); putserver (buffer); } void joinchannel (char name[CHANNELLEN]) { putserver ("JOIN "); putserver(name); putserver("\n\r"); } void partchannel (char name[CHANNELLEN], char reason[512]) { putserver ("PART "); putserver(name); putserver(" :"); putserver(reason); putserver("\n\r"); } void nick (char name[NICKLEN]) { putserver ("NICK "); putserver(name); putserver("\n\r"); } int serverpong(char data[]) { int i, u; char ping[15]; i = u =0; while (data[i] != ':') ++i; ++i; while ((ping[u] = data[i]) != '\0') { ++i; ++u; } putserver ("PONG :"); putserver (ping); putserver ("\n\r"); return 0; } void notice (char *victim, char *words) { putserver ("NOTICE "); putserver (victim); putserver (" :"); putserver (words); putserver ("\n\r"); } void privmsg (char *victim, char *words) { putserver ("PRIVMSG "); putserver (victim); putserver (" :"); putserver (words); putserver ("\n\r"); } void ctcpreply (char *victim, char *what) { putserver ("NOTICE "); putserver (victim); putserver (" :\01"); putserver (what); putserver ("\01"); putserver ("\n\r"); } void pong (char *server) { putserver ("PONG :"); puttime (); putserver ("\n\r"); } void dumpmessage (char *victim, char *message) { char buffer[512]; int i, j; i = 0; j = 0; while (i < strlen (message)) { j = 0; while ((message[i] != '\n') && (j < 512) && (message[i])) { buffer[j] = message[i]; j++; i++; } buffer[j] = 0; notice(victim, buffer); i++; } } /* Utility functions that actually do read/writes from the server. */ void putserver (char *text) { int l, j; l = strlen (text); j = write (serverlink, text, l); //write (1, text, l); if (j != l) { perror ("write:"); exit (3); } } char *getline () { static char buffer[513]; /* one more than defined maximum length */ int i, j; /* Save the user database at regular intervals. */ i = 1; buffer[i] = 0; while ((buffer[i - 1] != '\n') && (i < 512)) { j = read (serverlink, &(buffer[i]), 1); if (j <= 0) { perror ("read"); /* Restart server link */ relink = time(NULL) +60; buffer[i]=0; return &(buffer[1]); } if (j == 0) { /* I'm not happy with this. read() can actually return 0 for totally different reasons. Anyway, restart the server-link. */ relink = time(NULL) +60; buffer[i] = 0; return (&(buffer[i])); } i++; } buffer[i] = 0; /* DEBUG: printf("%s\n",&buffer[1]); */ return &(buffer[1]); } void startlink () { struct hostent *server; struct sockaddr_in server_addr; char line[1024]; char send[512]; struct hostent *vhost = NULL; struct sockaddr_in local_addr; if (serverlink) { close (serverlink); } server = gethostbyname (SERVER); serverlink = socket (AF_INET, SOCK_STREAM, 0); server_addr.sin_family = AF_INET; server_addr.sin_port = htons (6667); memcpy (&(server_addr.sin_addr.s_addr), server->h_addr, server->h_length); serverlink = socket (AF_INET, SOCK_STREAM, 0); vhost = gethostbyname(TSC_HOST); if(vhost == NULL) { printf("Looking up vhost failed.\n"); return; } memset(&local_addr, 0, sizeof(struct sockaddr_in)); local_addr.sin_family = AF_INET; memcpy (&(local_addr.sin_addr.s_addr), vhost->h_addr, vhost->h_length); if(bind(serverlink, (struct sockaddr *) &local_addr, sizeof(struct sockaddr_in))) { perror ("bind"); return; } if (connect (serverlink, (struct sockaddr *) &server_addr, sizeof (server_addr))) { perror ("connect"); exit (2); } /* Establish a client connection. */ putserver("USER " BOT_NAME " +iw " BOT_NAME " : Natasha Trojan Scan\n\r"); putserver("Nick " BOT_NAME "\n\r"); while (strstr(line, " 255 ") == NULL) { // 376 KANSKE strcpy(line, getline()); if(relink) return; if (strstr(line, "PING :") != NULL) { serverpong(strstr(line, "PING")); // printf("Ping<>Pong\n"); } // printf("Server: %s\n", line); } printf("Connected.\n"); sprintf(line, "%s@%s", Q_NICK, Q_HOST); sprintf(send, "AUTH %s %s", TSC_Q_AUTH, TSC_Q_PASS); privmsg(line, send); joinchannel(REPORTCHAN); } void reconnect (void) { putserver("QUIT :RC\n\r"); close(serverlink); sleep(1); startlink(); } void parse (char *buffer) { int i, j; if(strstr(buffer, "PING") != NULL) pong(NULL); strncpy(host, "", HOSTLEN); for (i = 0; i < MAX_ARGS; i++) { args[i][0] = 0; } argcount = 0; sender[0] = 0; command[0] = 0; // printf("Recv: %s", buffer); /* Skip over any leading spaces. */ for (i = 0; buffer[i] && (buffer[i] == ' '); i++); /* * "i" had better increase monatomically from here on, or we're in * a lot of trouble. :) */ /* Check to see who sent it. */ if (buffer[i] == ':') { j = 0; for (i++; buffer[i] && (buffer[i] != '!' && buffer[i] != ' '); i++) { sender[j] = buffer[i]; j++; } sender[j] = 0; if(buffer[i] == '!') { ++i; for(j = 0; buffer[i] != '@'; ++i) { username[j] = buffer[i]; ++j; } ++i; for(j = 0; buffer[i] != ' '; ++i) { host[j] = buffer[i]; ++j; } j = 0; } } if (!buffer[i]) { return; } /* Skip over leading spaces */ while (buffer[i] && (buffer[i] == ' ')) i++; j = 0; while ((buffer[i + j]) && !isspace (buffer[i + j]) && j < 512) j++; memcpy (command, buffer + i, j); command[j] = 0; /* * Good news, we got the command and the sender (probably). * Now all we need is the parameters and we're in business. :) */ i = i + j; while ((buffer[i]) && (buffer[i] != '\n') && (buffer[i] != '\r')) { while (buffer[i] == ' ') i++; /* Again, do a standard spaces-skip job. */ j = 0; if (buffer[i] == ':') { i++; /* Skip over the : */ while ((buffer[i] != 0) && (buffer[i] != '\n') && (buffer[i] != '\r')) { args[argcount][j] = buffer[i]; i++; /* Next char in both the source */ j++; /* and the destination strings */ } args[argcount][j] = 0; } else { while ((buffer[i] != 0) && (buffer[i] != '\n') && (buffer[i] != '\r') && (buffer[i] != ' ')) { args[argcount][j] = buffer[i]; i++; j++; } args[argcount][j] = 0; } argcount++; } for (j = 0; servtab[j].func != NULL; j++) { if (!strcmp (command, servtab[j].msg)) { servtab[j].func (); } } return; } int main (int argc, char **argv) { int rc; fd_set r; struct timeval tm; // signal(SIGHUP, songdone); printf("%s\n\nConnecting to MySQL database.\n", CTCP_VERSION_REPLY); dbconnect(); printf("Loading channels...\n"); loadchannels(); printf("Connecting...\n"); startlink (); printf("\nScan begin.\n"); if(argc == 2) cchannel = atoi(argv[1]); joinchannel(clist[cchannel].name); timenext = time(NULL) + STAYTIME; /* Main loop */ while (1) { FD_ZERO (&r); FD_SET (serverlink, &r); tm.tv_usec=50000; tm.tv_sec=0; if(timenext <= time(NULL) && relink == 0) { loopaction(); timenext = time(NULL) + STAYTIME; } if(relink <= time(NULL) && relink != 0) { relink = 0; if(serverlink) close(serverlink); startlink(); } /* MySQL keepalive */ if(lastmysql+200 < time(NULL)) { #ifdef DEBUG printf("Reconnect MySQL\n"); #endif mysql_close(sock); dbconnect(); } /* Wait quietly for something to happen. */ /* * This form of waiting is like a sleep() so it's not CPU-heavy. * Note that the way this is written, if the server doesn't send * anything and doesn't close the link, the bot will do exactly * nothing. */ if(relink == 0) { rc = select (serverlink + 1, &r, NULL, NULL, &tm); if (rc==-1) { if (errno!=EINTR) { perror("select!=EINTR"); } else { perror("select==EINTR"); } } else if (rc>0) { if (FD_ISSET(serverlink,&r)) { parse(getline()); } else { fprintf(stderr,"FD_ISSET unknown.\n"); } } } else sleep(1); } }