/***************************************************************************** * * $Id$ * Purpose ...............: mbtask - Internet BBS Chat (but it looks like...) * ***************************************************************************** * Copyright (C) 1997-2005 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 * 1971 BV IJmuiden * the Netherlands * * This file is part of MBSE BBS. * * This BBS is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * MBSE BBS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MBSE BBS; see the file COPYING. If not, write to the Free * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. *****************************************************************************/ #include "../config.h" #include "../lib/mbselib.h" #include "taskstat.h" #include "taskutil.h" #include "taskibc.h" #ifdef USE_EXPERIMENT int ibc_run = FALSE; /* Thread running */ extern int T_Shutdown; /* Program shutdown */ extern int internet; /* Internet status */ time_t scfg_time = (time_t)0; /* Servers config time */ time_t now; /* Current time */ ncs_list *ncsl = NULL; /* Neighbours list */ srv_list *servers = NULL; /* Active servers */ usr_list *users = NULL; /* Active users */ int ls; /* Listen socket */ struct sockaddr_in myaddr_in; /* Listen socket address */ struct sockaddr_in clientaddr_in; /* Remote socket address */ int changed = FALSE; /* Databases changed */ char crbuf[512]; /* Chat receive buffer */ char csbuf[512]; /* Chat send buffer */ int srvchg = FALSE; /* Is serverlist changed */ int usrchg = FALSE; /* Is userlist changed */ pthread_mutex_t b_mutex = PTHREAD_MUTEX_INITIALIZER; typedef enum {NCS_INIT, NCS_CALL, NCS_WAITPWD, NCS_CONNECT, NCS_HANGUP, NCS_FAIL, NCS_DEAD} NCSTYPE; static char *ncsstate[] = { (char *)"init", (char *)"call", (char *)"waitpwd", (char *)"connect", (char *)"hangup", (char *)"fail", (char *)"dead" }; /* * Internal prototypes */ void fill_ncslist(ncs_list **, char *, char *, char *); void dump_ncslist(void); void tidy_servers(srv_list **); void del_userbyserver(usr_list **, char *); void add_server(srv_list **, char *, int, char *, char *, char *, char *); void del_server(srv_list **, char *); void del_router(srv_list **, char *); int send_msg(int, struct sockaddr_in, char *, char *); void broadcast(char *, char *); void check_servers(void); void command_pass(char *, char *); void command_server(char *, char *); void command_squit(char *, char *); void receiver(struct servent *); /* * Add a server to the serverlist */ void fill_ncslist(ncs_list **fdp, char *server, char *myname, char *passwd) { ncs_list *tmp, *ta; int rc; if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "fill_ncslist() mutex_lock failed rc=%d", rc); tmp = (ncs_list *)malloc(sizeof(ncs_list)); memset(tmp, 0, sizeof(tmp)); tmp->next = NULL; strncpy(tmp->server, server, 63); strncpy(tmp->myname, myname, 63); strncpy(tmp->passwd, passwd, 15); tmp->state = NCS_INIT; tmp->action = now; tmp->last = (time_t)0; tmp->version = 0; tmp->remove = FALSE; tmp->socket = -1; tmp->token = 0; tmp->gotpass = FALSE; tmp->gotserver = FALSE; if (*fdp == NULL) { *fdp = tmp; } else { for (ta = *fdp; ta; ta = ta->next) if (ta->next == NULL) { ta->next = (ncs_list *)tmp; break; } } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "fill_ncslist() mutex_unlock failed rc=%d", rc); } void dump_ncslist(void) { ncs_list *tmp; srv_list *srv; usr_list *usrp; if (!changed && !srvchg) return; Syslog('r', "Server State Del Pwd Srv Next action"); Syslog('r', "------------------------------ ------- --- --- --- -----------"); for (tmp = ncsl; tmp; tmp = tmp->next) { Syslog('r', "%-30s %-7s %s %s %s %d", tmp->server, ncsstate[tmp->state], tmp->remove ? "yes":"no ", tmp->gotpass ? "yes":"no ", tmp->gotserver ? "yes":"no ", (int)tmp->action - (int)now); } if (srvchg) { Syslog('+', "IBC: Server Router Hops Users Connect time"); Syslog('+', "IBC: ------------------------- ------------------------- ----- ----- --------------------"); for (srv = servers; srv; srv = srv->next) { Syslog('+', "IBC: %-25s %-25s %5d %5d %s", srv->server, srv->router, srv->hops, srv->users, rfcdate(srv->connected)); } } if (usrchg) { Syslog('+', "IBC: Server User Nick Channel Cop Connect time"); Syslog('+', "IBC: ------------------------- ------------------------- --------- -------------------- --- --------------------"); for (usrp = users; usrp; usrp = usrp->next) { Syslog('+', "IBC: %-25s %-25s %-9s %-20s %s %s", usrp->server, usrp->realname, usrp->nick, usrp->channel, usrp->chanop ? "yes":"no ", rfcdate(usrp->connected)); } } srvchg = FALSE; usrchg = FALSE; changed = FALSE; } void tidy_servers(srv_list ** fdp) { srv_list *tmp, *old; for (tmp = *fdp; tmp; tmp = old) { old = tmp->next; free(tmp); } *fdp = NULL; } /* * Add one user to the userlist */ void add_user(char *server, char *nick, char *realname) { usr_list *tmp, *ta; srv_list *sl; int rc; Syslog('r', "add_user %s %s %s", server, nick, realname); for (ta = users; ta; ta = ta->next) { if ((strcmp(ta->server, server) == 0) && (strcmp(ta->realname, realname) == 0)) { Syslog('r', "duplicate, ignore"); return; } } if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "add_user() mutex_lock failed rc=%d", rc); tmp = (usr_list *)malloc(sizeof(usr_list)); memset(tmp, 0, sizeof(tmp)); tmp->next = NULL; strncpy(tmp->server, server, 63); strncpy(tmp->nick, nick, 9); strncpy(tmp->realname, realname, 36); tmp->connected = now; if (users == NULL) { users = tmp; } else { for (ta = users; ta; ta = ta->next) if (ta->next == NULL) { ta->next = (usr_list *)tmp; break; } } for (sl = servers; sl; sl = sl->next) { if (strcmp(sl->server, server) == 0) { sl->users++; srvchg = TRUE; } } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "add_user() mutex_unlock failed rc=%d", rc); usrchg = TRUE; } /* * Delete one user. */ void del_user(char *server, char *realname) { usr_list *ta, *tan; srv_list *sl; int rc; Syslog('r', "deluser %s %s", server, realname); if (users == NULL) return; if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "del_user() mutex_lock failed rc=%d", rc); for (ta = users; ta; ta = ta->next) { while ((tan = ta->next) && (strcmp(tan->server, server) == 0) && (strcmp(tan->realname, realname) == 0)) { ta->next = tan->next; free(tan); usrchg = TRUE; } ta->next = tan; } for (sl = servers; sl; sl = sl->next) { if ((strcmp(sl->server, server) == 0) && sl->users) { sl->users--; srvchg = TRUE; } } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "del_user() mutex_unlock failed rc=%d", rc); } void add_server(srv_list **fdp, char *name, int hops, char *prod, char *vers, char *fullname, char *router) { srv_list *tmp, *ta; int rc; Syslog('r', "add_server %s %d %s %s %s", name, hops, prod, vers, fullname); for (ta = *fdp; ta; ta = ta->next) { if (strcmp(ta->server, name) == 0) { Syslog('r', "duplicate, ignore"); return; } } if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "add_server() mutex_lock failed rc=%d", rc); tmp = (srv_list *)malloc(sizeof(srv_list)); memset(tmp, 0, sizeof(tmp)); tmp->next = NULL; strncpy(tmp->server, name, 63); strncpy(tmp->router, router, 63); strncpy(tmp->prod, prod, 20); strncpy(tmp->vers, vers, 20); strncpy(tmp->fullname, fullname, 35); tmp->connected = now; tmp->users = 0; tmp->hops = hops; if (*fdp == NULL) { *fdp = tmp; } else { for (ta = *fdp; ta; ta = ta->next) if (ta->next == NULL) { ta->next = (srv_list *)tmp; break; } } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "add_server() mutex_unlock failed rc=%d", rc); srvchg = TRUE; } /* * Delete server. */ void del_server(srv_list **fap, char *name) { srv_list *ta, *tan; int rc; Syslog('r', "delserver %s", name); if (*fap == NULL) return; if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "del_server() mutex_lock failed rc=%d", rc); for (ta = *fap; ta; ta = ta->next) { while ((tan = ta->next) && (strcmp(tan->server, name) == 0)) { ta->next = tan->next; free(tan); srvchg = TRUE; } ta->next = tan; } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "del_server() mutex_unlock failed rc=%d", rc); } /* * Delete router. */ void del_router(srv_list **fap, char *name) { srv_list *ta, *tan; int rc; Syslog('r', "delrouter %s", name); if (*fap == NULL) return; if ((rc = pthread_mutex_lock(&b_mutex))) Syslog('!', "del_router() mutex_lock failed rc=%d", rc); for (ta = *fap; ta; ta = ta->next) { while ((tan = ta->next) && (strcmp(tan->router, name) == 0)) { ta->next = tan->next; free(tan); srvchg = TRUE; } ta->next = tan; } if ((rc = pthread_mutex_unlock(&b_mutex))) Syslog('!', "del_router() mutex_unlock failed rc=%d", rc); } /* * Send a message to all servers */ void send_all(char *msg) { ncs_list *tnsl; sprintf(csbuf, "%s\r\n", msg); for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (tnsl->state == NCS_CONNECT) { send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } } /* * Broadcast a message to all servers except the originating server */ void broadcast(char *msg, char *origin) { ncs_list *tnsl; sprintf(csbuf, "%s\r\n", msg); for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if ((tnsl->state == NCS_CONNECT) && (strcmp(origin, tnsl->server))) { send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } } /* * Send message to a server */ int send_msg(int s, struct sockaddr_in servaddr, char *host, char *msg) { Syslog('r', "> %s: %s", host, printable(msg, 0)); if (sendto(s, msg, strlen(msg), 0, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) == -1) { Syslog('r', "$IBC: can't send message"); return -1; } return 0; } void check_servers(void) { char *errmsg, scfgfn[PATH_MAX]; FILE *fp; ncs_list *tnsl; int j, inlist; int a1, a2, a3, a4; struct servent *se; struct hostent *he; sprintf(scfgfn, "%s/etc/ibcsrv.data", getenv("MBSE_ROOT")); /* * Check if configuration is changed, if so then apply the changes. */ if (file_time(scfgfn) != scfg_time) { Syslog('r', "%s filetime changed, rereading"); if (servers == NULL) { /* * First add this server name to the servers database. */ add_server(&servers, CFG.myfqdn, 0, (char *)"mbsebbs", (char *)VERSION, CFG.bbs_name, (char *)"none"); } if ((fp = fopen(scfgfn, "r"))) { fread(&ibcsrvhdr, sizeof(ibcsrvhdr), 1, fp); while (fread(&ibcsrv, ibcsrvhdr.recsize, 1, fp)) { Syslog('r', "IBC server \"%s\", Active %s", ibcsrv.server, ibcsrv.Active ?"Yes":"No"); if (ibcsrv.Active) { inlist = FALSE; for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (strcmp(tnsl->server, ibcsrv.server) == 0) { inlist = TRUE; } } if (!inlist ) { Syslog('r', " not in neighbour list, add"); fill_ncslist(&ncsl, ibcsrv.server, ibcsrv.myname, ibcsrv.passwd); changed = TRUE; Syslog('+', "IBC: added Internet BBS Chatserver %s", ibcsrv.server); } } } /* * Now check for neighbours to delete */ for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { fseek(fp, ibcsrvhdr.hdrsize, SEEK_SET); inlist = FALSE; Syslog('r', "IBC server \"%s\"", ibcsrv.server); while (fread(&ibcsrv, ibcsrvhdr.recsize, 1, fp)) { if ((strcmp(tnsl->server, ibcsrv.server) == 0) && ibcsrv.Active) { inlist = TRUE; } } if (!inlist) { Syslog('r', " not in configuration, remove"); tnsl->remove = TRUE; tnsl->action = now; changed = TRUE; } } fclose(fp); } scfg_time = file_time(scfgfn); } dump_ncslist(); /* * Check if we need to make state changes */ for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (((int)tnsl->action - (int)now) <= 0) { switch (tnsl->state) { case NCS_INIT: Syslog('r', "%s init", tnsl->server); changed = TRUE; /* * If Internet is available, setup the connection. */ if (internet) { /* * Get IP address for the hostname, set default next action * to 60 seconds. */ tnsl->action = now + (time_t)60; memset(&tnsl->servaddr_in, 0, sizeof(struct sockaddr_in)); se = getservbyname("fido", "udp"); tnsl->servaddr_in.sin_family = AF_INET; tnsl->servaddr_in.sin_port = se->s_port; if (sscanf(tnsl->server,"%d.%d.%d.%d",&a1,&a2,&a3,&a4) == 4) tnsl->servaddr_in.sin_addr.s_addr = inet_addr(tnsl->server); else if ((he = gethostbyname(tnsl->server))) memcpy(&tnsl->servaddr_in.sin_addr, he->h_addr, he->h_length); else { switch (h_errno) { case HOST_NOT_FOUND: errmsg = (char *)"Authoritative: Host not found"; break; case TRY_AGAIN: errmsg = (char *)"Non-Authoritive: Host not found"; break; case NO_RECOVERY: errmsg = (char *)"Non recoverable errors"; break; default: errmsg = (char *)"Unknown error"; break; } Syslog('!', "IBC: no IP address for %s: %s", tnsl->server, errmsg); tnsl->action = now + (time_t)120; tnsl->state = NCS_FAIL; changed = TRUE; break; } tnsl->socket = socket(AF_INET, SOCK_DGRAM, 0); if (tnsl->socket == -1) { Syslog('!', "$IBC: can't create socket for %s", tnsl->server); tnsl->state = NCS_FAIL; tnsl->action = now + (time_t)120; changed = TRUE; break; } Syslog('r', "socket %d", tnsl->socket); tnsl->state = NCS_CALL; tnsl->action = now + (time_t)1; } else { tnsl->action = now + (time_t)10; } break; case NCS_CALL: /* * In this state we accept PASS and SERVER commands from * the remote with the same token as we have sent. */ Syslog('r', "%s call", tnsl->server); tnsl->token = gettoken(); sprintf(csbuf, "PASS %s 0000 IBC| %s\r\n", tnsl->passwd, tnsl->compress ? "Z":""); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); sprintf(csbuf, "SERVER %s 0 %ld mbsebbs %s %s\r\n", tnsl->myname, tnsl->token, VERSION, CFG.bbs_name); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); tnsl->action = now + (time_t)10; tnsl->state = NCS_WAITPWD; changed = TRUE; break; case NCS_WAITPWD: /* * This state can be left by before the timeout is reached * by a reply from the remote if the connection is accepted. */ Syslog('r', "%s waitpwd", tnsl->server); tnsl->token = 0; tnsl->state = NCS_CALL; while (TRUE) { j = 1+(int) (1.0 * CFG.dialdelay * rand() / (RAND_MAX + 1.0)); if ((j > (CFG.dialdelay / 10)) && (j > 9)) break; } Syslog('r', "next call in %d %d seconds", CFG.dialdelay, j); tnsl->action = now + (time_t)j; changed = TRUE; break; case NCS_CONNECT: /* * In this state we check if the connection is still alive */ if (((int)now - (int)tnsl->last) > 70) { Syslog('+', "IBC: server %s connection is dead", tnsl->server); tnsl->state = NCS_DEAD; tnsl->action = now + (time_t)120; // 2 minutes delay before calling again. tnsl->gotpass = FALSE; tnsl->gotserver = FALSE; tnsl->token = 0; del_router(&servers, tnsl->server); sprintf(csbuf, "SQUIT %s Connection died", tnsl->server); broadcast(csbuf, tnsl->server); changed = TRUE; break; } if (((int)now - (int)tnsl->last) > 60) { sprintf(csbuf, "PING\r\n"); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } tnsl->action = now + (time_t)10; break; case NCS_HANGUP: Syslog('r', "%s hangup => call", tnsl->server); tnsl->action = now + (time_t)1; tnsl->state = NCS_CALL; changed = TRUE; break; case NCS_DEAD: Syslog('r', "%s dead -> call", tnsl->server); tnsl->action = now + (time_t)1; tnsl->state = NCS_CALL; changed = TRUE; break; case NCS_FAIL: Syslog('r', "%s fail => init", tnsl->server); tnsl->action = now + (time_t)1; tnsl->state = NCS_INIT; changed = TRUE; break; } } } dump_ncslist(); } void command_pass(char *hostname, char *parameters) { ncs_list *tnsl; char *passwd, *version, *opts, *lnk; for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (strcmp(tnsl->server, hostname) == 0) { break; } } passwd = strtok(parameters, " \0"); version = strtok(NULL, " \0"); opts = strtok(NULL, " \0"); lnk = strtok(NULL, " \0"); // Syslog('r', "passwd \"%s\"", printable(passwd, 0)); // Syslog('r', "version \"%s\"", printable(version, 0)); // Syslog('r', "opts \"%s\"", printable(opts, 0)); // Syslog('r', "link \"%s\"", printable(lnk, 0)); if (version == NULL) { sprintf(csbuf, "461 PASS: Not enough parameters\r\n"); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); return; } if (strcmp(passwd, tnsl->passwd)) { Syslog('!', "IBC: got bad password %s from %s", passwd, hostname); return; } tnsl->gotpass = TRUE; tnsl->version = atoi(version); if (lnk && strchr(lnk, 'Z')) tnsl->compress = TRUE; changed = TRUE; } void command_server(char *hostname, char *parameters) { ncs_list *tnsl; srv_list *ta; char temp[512], *name, *hops, *id, *prod, *vers, *fullname; unsigned long token; int ihops, found = FALSE; name = strtok(parameters, " \0"); hops = strtok(NULL, " \0"); id = strtok(NULL, " \0"); prod = strtok(NULL, " \0"); vers = strtok(NULL, " \0"); fullname = strtok(NULL, "\0"); ihops = atoi(hops) + 1; for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (strcmp(tnsl->server, name) == 0) { found = TRUE; break; } } // Syslog('r', "name \"%s\"", printable(name, 0)); // Syslog('r', "hops \"%s\"", printable(hops, 0)); // Syslog('r', "id \"%s\"", printable(id, 0)); // Syslog('r', "prod \"%s\"", printable(prod, 0)); // Syslog('r', "vers \"%s\"", printable(vers, 0)); // Syslog('r', "full \"%s\"", printable(fullname, 0)); if (fullname == NULL) { sprintf(csbuf, "461 SERVER: Not enough parameters\r\n"); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); return; } token = atoi(id); sprintf(temp, "SERVER %s %d %s %s %s %s", name, ihops, id, prod, vers, fullname); if (found && tnsl->token) { /* * We are in calling state, so we expect the token from the * remote is the same as the token we sent. * In that case, the session is authorized. */ if (tnsl->token == token) { broadcast(temp, tnsl->server); tnsl->gotserver = TRUE; changed = TRUE; tnsl->state = NCS_CONNECT; tnsl->action = now + (time_t)10; Syslog('+', "IBC: connected with %s", tnsl->server); /* * Send all already known servers */ for (ta = servers; ta; ta = ta->next) { if (ta->hops) { sprintf(csbuf, "SERVER %s %d 0 %s %s %s\r\n", ta->server, ta->hops, ta->prod, ta->vers, ta->fullname); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } add_server(&servers, tnsl->server, ihops, prod, vers, fullname, hostname); return; } Syslog('r', "IBC: collision with %s", tnsl->server); return; } /* * We are in waiting state, so we sent our PASS and SERVER * messages and set the session to connected if we got a * valid PASS command. */ if (found && tnsl->gotpass) { sprintf(csbuf, "PASS %s 0000 IBC| %s\r\n", tnsl->passwd, tnsl->compress ? "Z":""); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); sprintf(csbuf, "SERVER %s 0 %ld mbsebbs %s %s\r\n", tnsl->myname, token, VERSION, CFG.bbs_name); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); broadcast(temp, tnsl->server); tnsl->gotserver = TRUE; tnsl->state = NCS_CONNECT; tnsl->action = now + (time_t)10; Syslog('+', "IBC: connected with %s", tnsl->server); /* * Send all already known servers */ for (ta = servers; ta; ta = ta->next) { if (ta->hops) { sprintf(csbuf, "SERVER %s %d 0 %s %s %s\r\n", ta->server, ta->hops, ta->prod, ta->vers, ta->fullname); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } add_server(&servers, tnsl->server, ihops, prod, vers, fullname, hostname); changed = TRUE; return; } if (! found) { /* * Got a message about a server that is not our neighbour. */ add_server(&servers, name, ihops, prod, vers, fullname, hostname); broadcast(temp, hostname); changed = TRUE; return; } Syslog('r', "IBC: got SERVER command without PASS command from %s", hostname); return; } void command_squit(char *hostname, char *parameters) { ncs_list *tnsl; char temp[512], *name, *message; for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (strcmp(tnsl->server, hostname) == 0) { break; } } name = strtok(parameters, " \0"); message = strtok(NULL, "\0"); if (strcmp(name, tnsl->server) == 0) { Syslog('+', "IBC: disconnect server %s: %s", name, message); tnsl->state = NCS_HANGUP; tnsl->action = now + (time_t)120; // 2 minutes delay before calling again. tnsl->gotpass = FALSE; tnsl->gotserver = FALSE; tnsl->token = 0; del_router(&servers, name); } else { Syslog('r', "IBC: disconnect server %s: message is not for us, but update database", name); del_server(&servers, name); } sprintf(temp, "SQUIT %s %s", name, message); broadcast(temp, hostname); changed = TRUE; } void receiver(struct servent *se) { struct pollfd pfd; struct hostent *hp; int rc, len, inlist; socklen_t sl; ncs_list *tnsl; char *hostname, *prefix, *command, *parameters; pfd.fd = ls; pfd.events = POLLIN; pfd.revents = 0; if ((rc = poll(&pfd, 1, 1000) < 0)) { Syslog('r', "$poll/select failed"); return; } if (pfd.revents & POLLIN || pfd.revents & POLLERR || pfd.revents & POLLHUP || pfd.revents & POLLNVAL) { sl = sizeof(myaddr_in); memset(&clientaddr_in, 0, sizeof(struct sockaddr_in)); memset(&crbuf, 0, sizeof(crbuf)); if ((len = recvfrom(ls, &crbuf, sizeof(crbuf)-1, 0,(struct sockaddr *)&clientaddr_in, &sl)) != -1) { hp = gethostbyaddr((char *)&clientaddr_in.sin_addr, sizeof(struct in_addr), clientaddr_in.sin_family); if (hp == NULL) hostname = inet_ntoa(clientaddr_in.sin_addr); else hostname = hp->h_name; if ((crbuf[strlen(crbuf) -2] != '\r') && (crbuf[strlen(crbuf) -1] != '\n')) { Syslog('r', "Message not terminated with CR-LF, dropped"); return; } inlist = FALSE; for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (strcmp(tnsl->server, hostname) == 0) { inlist = TRUE; break; } } if (!inlist) { Syslog('!', "IBC: message from unknown host (%s), dropped", hostname); return; } if (tnsl->state == NCS_INIT) { Syslog('r', "IBC: message received from %s while in init state, dropped", hostname); return; } tnsl->last = now; crbuf[strlen(crbuf) -2] = '\0'; Syslog('r', "< %s: \"%s\"", hostname, printable(crbuf, 0)); /* * Parse message */ if (crbuf[0] == ':') { prefix = strtok(crbuf, " "); command = strtok(NULL, " \0"); parameters = strtok(NULL, "\0"); } else { prefix = NULL; command = strtok(crbuf, " \0"); parameters = strtok(NULL, "\0"); } // Syslog('r', "prefix \"%s\"", printable(prefix, 0)); // Syslog('r', "command \"%s\"", printable(command, 0)); // Syslog('r', "parameters \"%s\"", printable(parameters, 0)); if (! strcmp(command, (char *)"PASS")) { if (parameters == NULL) { sprintf(csbuf, "461 %s: Not enough parameters\r\n", command); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } else { command_pass(hostname, parameters); } } else if (! strcmp(command, (char *)"SERVER")) { if (parameters == NULL) { sprintf(csbuf, "461 %s: Not enough parameters\r\n", command); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } else { command_server(hostname, parameters); } } else if (! strcmp(command, (char *)"PING")) { sprintf(csbuf, "PONG\r\n"); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } else if (! strcmp(command, (char *)"PONG")) { /* * Just accept */ } else if (! strcmp(command, (char *)"SQUIT")) { if (parameters == NULL) { sprintf(csbuf, "461 %s: Not enough parameters\r\n", command); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } else { command_squit(hostname, parameters); } } else if (atoi(command)) { Syslog('r', "IBC: Got error %d", atoi(command)); } else if (tnsl->state == NCS_CONNECT) { /* * Only if connected we send a error response */ sprintf(csbuf, "421 %s: Unknown command\r\n", command); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } else { Syslog('r', "recvfrom returned len=%d", len); } } } /* * IBC thread */ void *ibc_thread(void *dummy) { struct servent *se; ncs_list *tnsl; Syslog('+', "Starting IBC thread"); if ((se = getservbyname("fido", "udp")) == NULL) { Syslog('!', "IBC: no fido udp entry in /etc/services, cannot start Internet BBS Chat"); goto exit; } myaddr_in.sin_family = AF_INET; myaddr_in.sin_addr.s_addr = INADDR_ANY; myaddr_in.sin_port = se->s_port; Syslog('+', "IBC: listen on %s, port %d", inet_ntoa(myaddr_in.sin_addr), ntohs(myaddr_in.sin_port)); ls = socket(AF_INET, SOCK_DGRAM, 0); if (ls == -1) { Syslog('!', "$IBC: can't create listen socket"); goto exit; } if (bind(ls, (struct sockaddr *)&myaddr_in, sizeof(struct sockaddr_in)) == -1) { Syslog('!', "$IBC: can't bind listen socket"); goto exit; } ibc_run = TRUE; srand(getpid()); while (! T_Shutdown) { now = time(NULL); /* * Check neighbour servers state */ check_servers(); /* * Get any incoming messages */ receiver(se); } Syslog('r', "IBC: start shutdown connections"); for (tnsl = ncsl; tnsl; tnsl = tnsl->next) { if (tnsl->state == NCS_CONNECT) { sprintf(csbuf, "SQUIT %s System shutdown\r\n", tnsl->myname); send_msg(tnsl->socket, tnsl->servaddr_in, tnsl->server, csbuf); } } tidy_servers(&servers); exit: ibc_run = FALSE; Syslog('+', "IBC thread stopped"); pthread_exit(NULL); } #endif