#include "ircbouncer.h" /* liste des ip autorisées à la connection */ char *ipauth[] = IPAUTH; struct endstruct clientstruct, serverstruct; void log(char *fmt, ...); /* {{{ ConsoleColor */ #define BLACK 0 #define RED 1 #define GREEN 2 #define YELLOW 3 #define BLUE 4 #define MAGENTA 5 #define CYAN 6 #define WHITE 7 void ConsoleColor(console, color) { char command[13]; int len; len = snprintf(command, 12, "%c[%d;%d;%dm", 0x1B, 0, color + 30, 40); write(console, command, len); } /* }}} */ /* {{{ * endstruct_[re]init */ /* initialise tous les champs d'une structure endstruct */ void endstruct_reinit(struct endstruct *lpes) { int r; struct liste_phrases *lplp, *lplp2; /* on commence par chopper tous les mutexes a la fois */ while (1) { usleep(50000); pthread_mutex_lock(&lpes->i_phrase_mutex); if (!(r = pthread_mutex_trylock(&lpes->o_phrase_mutex))) { break; } pthread_mutex_unlock(&lpes->i_phrase_mutex); if (r != EBUSY) { /* couille dans le paté */ log("Error : pthread_mutex_trylock() : %s", strerror(errno)); iorunning = 0; masterrunning = 0; return; } } /* on a la structure, on peut initialiser toutes les valeurs */ lpes->socketdesc = -1; lpes->i_buff.buff_start = 0; lpes->i_buff.buff_end = 0; lpes->o_buff.buff_start = 0; lpes->o_buff.buff_end = 0; /* on free() les phrases */ lplp = lpes->i_phrase_tete; while (lplp) { free(lplp->phrase); lplp2 = lplp->suivante; free(lplp); lplp = lplp2; } lpes->i_phrase_tete = NULL; lpes->i_phrase_queue = NULL; /* on free() les phrases */ lplp = lpes->o_phrase_tete; while (lplp) { free(lplp->phrase); lplp2 = lplp->suivante; free(lplp); lplp = lplp2; } lpes->o_phrase_tete = NULL; lpes->o_phrase_queue = NULL; /* opération terminée avec succès */ pthread_mutex_unlock(&lpes->o_phrase_mutex); pthread_mutex_unlock(&lpes->i_phrase_mutex); } /* initialise les semaphores d'une structure endstruct et la reinitialise */ void endstruct_init(struct endstruct *lpes) { if ((pthread_mutex_init(&lpes->i_phrase_mutex, NULL)) || (pthread_mutex_init(&lpes->o_phrase_mutex, NULL)) ) { log("Error : pthread_mutex_init() : %s", strerror(errno)); iorunning = 0; masterrunning = 0; return; } lpes->i_phrase_tete = NULL; lpes->o_phrase_tete = NULL; endstruct_reinit(lpes); } /* }}} */ /* {{{ * trynewclient */ /* regarde si le nouveau client qui tente d'établir une connection est légitime ou non, si oui met a jour clientstruct */ void trynewclient(void) { char *correspondant; int i = 0; int newsock; struct sockaddr_in sa; socklen_t sa_len = sizeof (struct sockaddr_in); newsock = accept(listenedsocket, (struct sockaddr *) &sa, &sa_len); if (newsock == -1) return; correspondant = strdup(inet_ntoa(sa.sin_addr)); log("Connection de %s... ", correspondant); while (ipauth[i] != NULL) { /* on regarde si la connection est autorisée */ if (!strcmp(correspondant, ipauth[i])) { /* si c'est le cas on reinitialise la structure clientstruct */ endstruct_reinit(&clientstruct); break; } i++; } if (ipauth[i] == NULL) { /* la connection est rejetée */ close(newsock); log("Connection rejetée (hote non autorisé)"); } else { newclient = 1; clientstruct.socketdesc = newsock; log("Connection acceptée"); } free(correspondant); } /* }}} */ /* {{{ * getphrase, putphrase */ /* vide i_buff et essaye de construire une phrase complete avec ca */ void getphrase(struct endstruct *lpes) { static char phrasetmp[PHLEN]; static int tmplen = 0; struct raw_buff *lprb = &lpes->i_buff; struct liste_phrases *lplp; while (lprb->buff_start < lprb->buff_end) { if (tmplen >= PHLEN) { log("La phrase en cours dépasse la longueur maximale %d\n'%.*s'\n", PHLEN, PHLEN, phrasetmp); tmplen = 0; } if ((phrasetmp[tmplen++] = lprb->buff[lprb->buff_start++]) == '\n') { /* on a une phrase complete, on la rajoute en queue de liste */ /* on alloue le maillon */ lplp = (struct liste_phrases *) malloc(sizeof (struct liste_phrases)); if (!lplp) { log("Erreur : getphrase : malloc() : %s", strerror(errno)); return; } /* on alloue la chaine du maillon (lol ;) */ lplp->phrase = (char *) malloc(tmplen + 1); if (!lplp->phrase) { log("Erreur : getphrase : malloc() : %s", strerror(errno)); free(lplp); return; } strncpy(lplp->phrase, phrasetmp, tmplen); tmplen = 0; /* et on insere le maillon en queue de chaine */ lplp->suivante = NULL; pthread_mutex_lock(&lpes->i_phrase_mutex); lplp->precedente = lpes->i_phrase_queue; lpes->i_phrase_queue = lplp; /* en gardant le chainage double */ if (lplp->precedente) lplp->precedente->suivante = lplp; else lpes->i_phrase_tete = lplp; pthread_mutex_unlock(&lpes->i_phrase_mutex); break; } } if (lprb->buff_start && (lprb->buff_start == lprb->buff_end)) lprb->buff_start = lprb->buff_end = 0; } /* envoie la plus ancienne phrase de la liste o_phrase dans le socket */ void putphrase(struct endstruct *lpes) { static char phrasetmp[PHLEN]; static int tmplen = 0; static int phraseencours = 0; struct raw_buff *lprb = &lpes->o_buff; struct liste_phrases *lplp = lpes->o_phrase_queue; if (!phraseencours) { if (!lplp) return; else { tmplen = 0; phraseencours = 1; pthread_mutex_lock(&lpes->o_phrase_mutex); /* on fait une copie locale de la phrase */ strncpy(phrasetmp, lplp->phrase, PHLEN); /* et on l'efface de la liste */ free(lplp->phrase); lpes->o_phrase_queue = lplp->precedente; if (lpes->o_phrase_queue) lpes->o_phrase_queue->suivante = NULL; else lpes->o_phrase_tete = NULL; pthread_mutex_unlock(&lpes->o_phrase_mutex); free(lplp); } } while (lprb->buff_end < BLEN) if (((lprb->buff[lprb->buff_end++] = phrasetmp[tmplen++]) == '\n') || (tmplen >= PHLEN)) { phraseencours = 0; break; } } /* }}} */ /* {{{ * flushget, flushsend */ /* recupere ce qu'on peut depuis le socket dans i_buff */ void flushget(struct endstruct *lpes) { int r; char *me; struct raw_buff *lprb = &lpes->i_buff; if (lpes == &clientstruct) { me = "client"; ConsoleColor(3, BLUE); } else { me = "server"; ConsoleColor(3, YELLOW); } r = read(lpes->socketdesc, lprb->buff + lprb->buff_end, BLEN - lprb->buff_end); if (r <= 0) { /* on a perdu la connection avec le serveur */ log("Info : perdu %s : %s", me, strerror(errno)); lpes->socketdesc = -1; } else { write(3, lprb->buff + lprb->buff_end, r); lprb->buff_end += r; } } /* envoie ce qu'on peut de ce qui est dans o_raw */ void flushsend(struct endstruct *lpes) { int r; char *me; struct raw_buff *lprb = &lpes->o_buff; if (lpes == &clientstruct) { me = "client"; ConsoleColor(3, CYAN); } else { me = "server"; ConsoleColor(3, WHITE); } r = write(lpes->socketdesc, lprb->buff + lprb->buff_start, lprb->buff_end - lprb->buff_start); if (r <= 0) { /* on a perdu la liaison */ log("Info : perdu %s : %s", me, strerror(errno)); lpes->socketdesc = -1; } else { write(3, lprb->buff + lprb->buff_start, r); lprb->buff_start += r; if (lprb->buff_start == lprb->buff_end) lprb->buff_start = lprb->buff_end = 0; } } /* }}} */ /* {{{ * io_thread */ void * io_thread(void *dummy) { struct timeval tv; fd_set RD, WR; int selectmaxfd; int r; /* boucle principale */ while (iorunning || clientstruct.o_phrase_queue || serverstruct.o_phrase_queue) { FD_ZERO(&RD); FD_ZERO(&WR); selectmaxfd = 0; /* mise en place des listes de sockets surveillés */ if (clientstruct.socketdesc >= 0) { /* si on est connecté avec le client */ /* on regarde si on peut lire une phrase complete */ getphrase(&clientstruct); if (clientstruct.i_buff.buff_end < BLEN) { /* on l'écoute si il reste de la place dans le buffer */ FD_SET(clientstruct.socketdesc, &RD); selectmaxfd = max(selectmaxfd, clientstruct.socketdesc); } putphrase(&clientstruct); if (clientstruct.o_buff.buff_end - clientstruct.o_buff.buff_start > 0) { /* on regarde si il faut lui dire quelquechose */ FD_SET(clientstruct.socketdesc, &WR); selectmaxfd = max(selectmaxfd, clientstruct.socketdesc); } } if (serverstruct.socketdesc >= 0) { getphrase(&serverstruct); if (serverstruct.i_buff.buff_end < BLEN) { FD_SET(serverstruct.socketdesc, &RD); selectmaxfd = max(selectmaxfd, serverstruct.socketdesc); } putphrase(&serverstruct); if (serverstruct.o_buff.buff_end - serverstruct.o_buff.buff_start > 0) { FD_SET(serverstruct.socketdesc, &WR); selectmaxfd = max(selectmaxfd, serverstruct.socketdesc); } } if (listenedsocket >= 0) { /* si on attend une connection entrante */ FD_SET(listenedsocket, &RD); selectmaxfd = max(selectmaxfd, listenedsocket); } tv.tv_sec = 0; tv.tv_usec = 2000; /* select */ r = select(selectmaxfd + 1, &RD, &WR, NULL, &tv); if (!r || (r == -1 && errno == EINTR)) continue; if (r < 0) { log("Erreur : select() : %s", strerror(errno)); iorunning = 0; break; } if ((clientstruct.socketdesc >= 0) && FD_ISSET(clientstruct.socketdesc, &RD)) flushget(&clientstruct); if ((clientstruct.socketdesc >= 0) && FD_ISSET(clientstruct.socketdesc, &WR)) flushsend(&clientstruct); if ((serverstruct.socketdesc >= 0) && FD_ISSET(serverstruct.socketdesc, &RD)) flushget(&serverstruct); if ((serverstruct.socketdesc >= 0) && FD_ISSET(serverstruct.socketdesc, &WR)) flushsend(&serverstruct); if ((listenedsocket >= 0) && FD_ISSET(listenedsocket, &RD)) trynewclient(); } masterrunning = 0; exit(1); } /* }}} */