/* accept a connection on the control port */ static void do_accept(void) { int d; imc_connect *c; struct sockaddr_in sa; int size = sizeof(sa); int r; d=accept(control, (struct sockaddr *) &sa, &size); if (d<0) { imc_lerror("accept"); return; } r=fcntl(d, F_GETFL, 0); if (r<0 || fcntl(d, F_SETFL, O_NONBLOCK | r)<0) { imc_lerror("do_accept: fcntl"); close(d); return; } c=imc_new_connect(); c->state = IMC_WAIT1; c->desc = d; imc_add_event(IMC_LOGIN_TIMEOUT, ev_login_timeout, c, 1); imc_logstring("connection from %s:%d on descriptor %d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port), d); }
/* try sending a qnode */ void ev_qnode_send(void *data) { imc_qnode *p=(imc_qnode *)data; imc_packet out; save_ml(); save_mq(); /* send it.. */ imc_initdata(&out.data); sprintf(out.to, "Mail-daemon@%s", p->tomud); strcpy(out.from, "Mail-daemon"); strcpy(out.type, "mail"); imc_addkey(&out.data, "to", p->data->to); imc_addkey(&out.data, "from", p->data->from); imc_addkey(&out.data, "subject", p->data->subject); imc_addkey(&out.data, "date", p->data->date); imc_addkey(&out.data, "text", p->data->text); imc_addkey(&out.data, "id", p->data->id); imc_send(&out); imc_freedata(&out.data); /* try resending it in an hour */ imc_add_event(3600, ev_qnode_send, data, 1); }
/* global init */ void icec_init(void) { imc_logstring("ICE client starting."); icec_recv_chain = imc_recv_hook; imc_recv_hook = icec_recv; imc_add_event(60, ev_icec_firstrefresh, NULL, 1); icec_load_channels(); }
/* load mailqueue, assumes init_mq done */ static void load_mq(void) { FILE *in; imc_qnode *p; char name[200]; int when=10; imc_sncpy(name, imc_prefix, 189); strcat(name, "mail-queue"); in=fopen(name, "r"); if (!in) return; p=read_qnode(in); while (!feof(in) && !ferror(in)) { if (p) { add_mq(p); imc_add_event(when, ev_qnode_send, p, 1); when += rand()%30+30; } p=read_qnode(in); } fclose(in); }
void icec_recv_update(const char *from, const char *chan, const char *owner, const char *operators, const char *policy, const char *invited, const char *excluded) { ice_channel *c; const char *mud; mud = imc_mudof(from); /* forged? */ if(!strchr(chan, ':') || strcasecmp(mud, ice_mudof(chan))) { return; } c = icec_findchannel(chan); if(!c) { c = imc_malloc(sizeof(*c)); c->name = imc_strdup(chan); c->owner = imc_strdup(owner); c->operators = imc_strdup(operators); c->invited = imc_strdup(invited); c->excluded = imc_strdup(excluded); c->local = NULL; c->active = NULL; c->next = icec_channel_list; icec_channel_list = c; } else { imc_strfree(c->owner); imc_strfree(c->operators); imc_strfree(c->invited); imc_strfree(c->excluded); c->name = imc_strdup(chan); c->owner = imc_strdup(owner); c->operators = imc_strdup(operators); c->invited = imc_strdup(invited); c->excluded = imc_strdup(excluded); } if(!strcasecmp(policy, "open")) { c->policy = ICE_OPEN; } else if(!strcasecmp(policy, "closed")) { c->policy = ICE_CLOSED; } else { c->policy = ICE_PRIVATE; } if(c->local && !ice_audible(c, imc_name)) { icec_localfree(c); } icec_notify_update(c); imc_cancel_event(ev_icec_timeout, c); imc_add_event(ICEC_TIMEOUT, ev_icec_timeout, c, 0); }
/* imc_mail_startup: start up the mail subsystem */ void imc_mail_startup(void) { init_mq(); init_ml(); init_idlist(); /* order is important here: we need the maillist to resolve the ID refs in * the mailqueue */ load_ml(); load_mq(); load_idlist(); /* queue an expiry event */ imc_add_event(24*3600, ev_mailid_expire, NULL, 0); }
/* send a keepalive to everyone */ void imc_send_keepalive(void) { imc_packet out; if (imc_active<IA_UP) return; imc_initdata(&out.data); strcpy(out.type, "is-alive"); strcpy(out.from, "*"); strcpy(out.to, "*@*"); imc_addkey(&out.data, "versionid", IMC_VERSIONID); if (imc_siteinfo.flags[0]) imc_addkey(&out.data, "flags", imc_siteinfo.flags); imc_send(&out); imc_freedata(&out.data); imc_add_event(ICED_REFRESH_TIME, ev_iced_refresh, NULL, 1); }
/* put a line onto descriptors output buffer */ static void do_send(imc_connect *c, const char *line) { int len; char *newbuf; int newsize=c->outsize; if (c->state==IMC_CLOSED) return; // imc_debug(c, 1, line); /* log outgoing traffic */ if (!c->outbuf[0]) c->newoutput=1; len=strlen(c->outbuf)+strlen(line)+3; if (len > c->outsize) { #ifdef SHOW_OVERFLOW /* not an error anymore, expected and handled - shogar */ if (len > IMC_MAXBUF) { if (!c->info || !(c->info->flags & IMC_QUIET)) imc_logerror("%s: output buffer overflow", imc_getconnectname(c)); imc_logerror("%d: was allocated", c->outsize); // imc_logerror("current buf: %s", c->outbuf); // do_close(c); // imc_free(c->outbuf,c->outsize); // c->outsize=IMC_MINBUF; // c->outbuf= imc_malloc(c->outsize); // len=strlen(line)+3; // return; } #endif while(newsize < len) newsize*=2; newbuf=imc_malloc(newsize); strcpy(newbuf, c->outbuf); imc_free(c->outbuf, c->outsize); c->outbuf=newbuf; c->outsize=newsize; } if (len<c->outsize/2 && len >= IMC_MINBUF) { newsize=c->outsize/2; newbuf=imc_malloc(newsize); strcpy(newbuf, c->outbuf); imc_free(c->outbuf, c->outsize); c->outbuf=newbuf; c->outsize=newsize; } strcat(c->outbuf, line); strcat(c->outbuf, "\n\r"); if (strlen(c->outbuf)>=c->outsize/2) { imc_cancel_event(ev_shrink_output, c); imc_add_event(IMC_SHRINKTIME, ev_shrink_output, c, 0); } }
/* read waiting data from descriptor. * read to a temp buffer to avoid repeated allocations */ static void do_read(imc_connect *c) { int size; int r; char temp[IMC_MAXBUF]; char *newbuf; int newsize; r=read(c->desc, temp, IMC_MAXBUF-1); if (!r || (r<0 && errno != EAGAIN && errno != EWOULDBLOCK)) { if (!c->info || !(c->info->flags & IMC_QUIET)) { if (r<0) /* read error */ { imc_lerror("%s: read", imc_getconnectname(c)); } else /* socket was closed */ { imc_logerror("%s: read: EOF", imc_getconnectname(c)); } } do_close(c); return; } if (r<0) /* EAGAIN error */ return; temp[r]=0; size=strlen(c->inbuf)+r+1; if (size>=c->insize) { #ifdef SHOW_OVERFLOW /* not an error anymore, expected and handled - shogar */ if (size>IMC_MAXBUF) { imc_logerror("%s: input buffer overflow", imc_getconnectname(c)); imc_logerror("%d: was allocated", c->insize); // do_close(c); // imc_free(c->inbuf,c->insize); // c->insize=IMC_MINBUF; // c->inbuf= imc_malloc(c->insize); // size = r + 1; // return; } #endif newsize=c->insize; while(newsize<size) newsize*=2; newbuf=imc_malloc(newsize); strcpy(newbuf, c->inbuf); imc_free(c->inbuf, c->insize); c->inbuf=newbuf; c->insize=newsize; } if (size>c->insize/2) { imc_cancel_event(ev_shrink_input, c); imc_add_event(IMC_SHRINKTIME, ev_shrink_input, c, 0); } if (size<c->insize/2 && size >= IMC_MINBUF) { newsize=c->insize; newsize/=2; newbuf=imc_malloc(newsize); strcpy(newbuf, c->inbuf); imc_free(c->inbuf, c->insize); c->inbuf=newbuf; c->insize=newsize; } strcat(c->inbuf, temp); imc_stats.rx_bytes += r; }
/* close given connection */ static void do_close(imc_connect *c) { const char *name; imc_reminfo *r; if (c->info) /* if our switched hub, get a new one right away - shogar */ { if (!imc_is_router && (c->info->flags & IMC_NEW_HUB) && c->info == imc_info_list) { imc_cancel_event(ev_imc_pollforhub,NULL); imc_cancel_event(ev_imc_optimize,NULL); imc_add_event(10, ev_imc_pollforhub, NULL, 1); imc_add_event(70, ev_imc_optimize, NULL, 1); } } if (c->state==IMC_CLOSED) return; name=imc_getconnectname(c); if(name && c->state == IMC_CONNECTED) /* dont send if never connected */ { imc_close_notify(name); close(c->desc); /* dont close if closed */ } if (c->state == IMC_CONNECTED) c->info->connection=NULL; /* handle reconnects */ if (c->info) if ((c->info->flags & IMC_RECONNECT) && !(c->info->flags & IMC_DENY) && !(c->info->flags & IMC_CLIENT)) { imc_setup_reconnect(c->info); } c->state=IMC_CLOSED; /* only log after we've set the state, in case imc_logstring * sends packets itself (problems with eg. output buffer overflow). */ if (!c->info || !(c->info->flags & IMC_QUIET)) { name=imc_getconnectname(c); if(name) imc_logstring("%s: closing link", name); } if (c->info) { r=imc_find_reminfo(c->info->name, 1); if (r) imc_delete_reminfo(r); } if(c->desc) close(c->desc); c->desc=0; c->inbuf[0]=0; c->outbuf[0]=0; }
/* connect to given mud */ int imc_connect_to(const char *mud) { imc_info *i; imc_connect *c; int desc; struct sockaddr_in sa; char buf[IMC_DATA_LENGTH]; int r; if (imc_active == IA_NONE) { imc_qerror("IMC is not active"); return 0; } i=imc_getinfo(mud); if (!i) { imc_qerror("%s: unknown mud name", mud); return 0; } if (i->connection) { imc_qerror("%s: already connected", mud); return 0; } if (i->flags & IMC_CLIENT) { imc_qerror("%s: client-only flag is set", mud); return 0; } if (i->flags & IMC_DENY) { imc_qerror("%s: deny flag is set", mud); return 0; } if (!(i->flags & IMC_QUIET)) imc_logstring("connect to %s", mud); /* warning: this blocks. It would be better to farm the query out to * another process, but that is difficult to do without lots of changes * to the core mud code. You may want to change this code if you have an * existing resolver process running. */ if ((sa.sin_addr.s_addr=inet_addr(i->host)) == -1UL) { struct hostent *hostinfo; if (NULL == (hostinfo=gethostbyname(i->host))) { imc_logerror("imc_connect: couldn't resolve hostname"); return 0; } sa.sin_addr.s_addr = *(unsigned long *) hostinfo->h_addr; } sa.sin_port = htons(i->port); sa.sin_family = AF_INET; desc=socket(AF_INET, SOCK_STREAM, 0); if (desc<0) { imc_lerror("socket"); return 0; } r=fcntl(desc, F_GETFL, 0); if (r<0 || fcntl(desc, F_SETFL, O_NONBLOCK | r)<0) { imc_lerror("imc_connect: fcntl"); close(desc); return 0; } if (connect(desc, (struct sockaddr *)&sa, sizeof(sa))<0) if (errno != EINPROGRESS) { imc_lerror("connect"); close(desc); return 0; } c=imc_new_connect(); c->desc = desc; c->state = IMC_CONNECTING; c->info = i; imc_add_event(IMC_LOGIN_TIMEOUT, ev_login_timeout, c, 1); sprintf(buf, "PW %s %s version=%d", imc_name, i->clientpw, IMC_VERSION); do_send(c, buf); return 1; }
/* low-level idle function: read/write buffers as needed, etc */ void imc_idle_select(fd_set *read, fd_set *write, fd_set *exc, time_t now) { const char *command; imc_packet *p; imc_connect *c, *c_next ; if (imc_active<IA_CONFIG1) return; if (imc_lock) { imc_logerror("imc_idle_select: recursive call"); return; } imc_lock=1; if (imc_sequencenumber < (unsigned long)imc_now) imc_sequencenumber=(unsigned long)imc_now; imc_run_events(now); if (imc_active<IA_UP) { imc_lock=0; return; } /* handle results of the select */ if (imc_active >= IA_LISTENING && FD_ISSET(control, read)) do_accept(); for (c=imc_connect_list; c; c=c_next) { c_next=c->next; if (c->state!=IMC_CLOSED && FD_ISSET(c->desc, exc)) do_close(c); if (c->state!=IMC_CLOSED && FD_ISSET(c->desc, read)) do_read(c); while (c->state!=IMC_CLOSED && // (c->spamtime1>=0 || c->spamcounter1<=IMC_SPAM1MAX) && // (c->spamtime2>=0 || c->spamcounter2<=IMC_SPAM2MAX) && (command = get_one_line(c->inbuf)) != NULL) { if (strlen(command) > imc_stats.max_pkt) imc_stats.max_pkt=strlen(command); // imc_debug(c, 0, command); /* log incoming packets */ switch (c->state) { case IMC_CLOSED: break; case IMC_WAIT1: clientpassword(c, command); break; case IMC_WAIT2: serverpassword(c, command); break; case IMC_CONNECTED: p = do_interpret_packet(c, command); if (p) { #ifdef IMC_PARANOIA /* paranoia: check the last entry in the path is the same as the * sending mud. Also check the first entry to see that it matches * the sender. */ imc_stats.rx_pkts++; if (strcasecmp(c->info->name, imc_lastinpath(p->i.path))) imc_logerror("PARANOIA: packet from %s allegedly from %s", c->info->name, imc_lastinpath(p->i.path)); else if (strcasecmp(imc_mudof(p->i.from), imc_firstinpath(p->i.path))) imc_logerror("PARANOIA: packet from %s has firstinpath %s", p->i.from, imc_firstinpath(p->i.path)); else forward(p); /* only forward if its a valid packet! */ #else imc_stats.rx_pkts++; forward(p); #endif #ifdef SPAMPROT if (!strcasecmp(p->type, "chat") || !strcasecmp(p->type, "tell") || !strcasecmp(p->type, "emote") || 1) { if (!c->spamcounter1 && !c->spamtime1) imc_add_event(IMC_SPAM1INTERVAL, ev_spam1, c, 0); c->spamcounter1++; if (!c->spamcounter2 && !c->spamtime2) imc_add_event(IMC_SPAM2INTERVAL, ev_spam2, c, 0); c->spamcounter2++; } #endif imc_freedata(&p->data); } break; } } } for (c=imc_connect_list; c; c=c_next) { c_next=c->next; if (c->state!=IMC_CLOSED && (FD_ISSET(c->desc, write) || c->newoutput)) { // c->newoutput=0; do_write(c); c->newoutput=c->outbuf[0]; } } for (c=imc_connect_list; c; c=c_next) { c_next=c->next; if (c->state==IMC_CLOSED) imc_extract_connect(c); } imc_lock=0; }
/* start up IMC */ void imc_startup_network(void) { imc_info *info; int toggle=0; /* This is to tell after we've done reconnects if we've connected to a hub or not -- Scion */ if (imc_active != IA_CONFIG2) { imc_logerror("imc_startup_network: called with imc_active==%d", imc_active); return; } if (!imc_siteinfo.name[0]) { imc_logerror("InfoName not set, not initializing"); return; } if (!imc_siteinfo.email[0]) { imc_logerror("InfoEmail not set, not initializing"); return; } imc_logstring("network initializing"); imc_active=IA_UP; control=-1; if (imc_port) imc_startup_port(); imc_stats.start = imc_now; imc_stats.rx_pkts = 0; imc_stats.tx_pkts = 0; imc_stats.rx_bytes = 0; imc_stats.tx_bytes = 0; imc_stats.sequence_drops = 0; imc_add_event(20, ev_keepalive, NULL, 1); /* fill my imclist please - shogar */ imc_add_event(30, ev_request_keepalive, NULL, 1); imc_mail_startup(); /* start up the mailer */ if (!lock_prefix()) { imc_logstring("another process is using the same config prefix, not autoconnecting."); return; } /* do autoconnects */ for (info=imc_info_list; info; info=info->next) if (!(info->flags & IMC_NOAUTO) && !(info->flags & IMC_CLIENT) && !(info->flags & IMC_DENY) /* && !(info->flags & IMC_OLD_HUB) not used anymore - shogar && !(info->flags & IMC_DEAD_HUB) */ && !(info->flags & IMC_HUB)) { if (imc_connect_to(info->name) && ((info->flags & IMC_MAIN_HUB) || (info->flags & IMC_HUB))) toggle=1; } /* Setup optimization to run, it won't if we're a hub -- Scion */ if(imc_hubswitch && !imc_is_router) { imc_add_event(360, ev_imc_pollforhub, NULL, 1); imc_add_event(420, ev_imc_optimize, NULL, 1); imc_logstring("Setting auto-optimize to run in 6 minutes."); } /* Are we connected to any hubs, btw? -- Scion */ if (!toggle) { /* No hubs connected! */ for (info=imc_info_list; info; info=info->next) if ((info->flags & IMC_HUB) && (!toggle)) if (imc_connect_to(info->name)) toggle=0; } /* We're out of ideas, notify the admins -- Scion */ /* if (!toggle) imc_logstring("No hubs connected! Check the configuration!"); */ }
/* expire old entries in the mailid list; called once an hour */ void ev_mailid_expire(void *data) { flush_idlist(imc_now + 24*3600); imc_add_event(3600, ev_mailid_expire, NULL, 1); }
/* imc_send_mail: called by the mud to add a piece of mail to the queue */ void imc_send_mail(const char *from, const char *to, const char *date, const char *subject, const char *text) { char temp[IMC_DATA_LENGTH]; imc_mail *m; imc_qnode *qroot, *q; char arg[IMC_NAME_LENGTH]; const char *mud; int when=10; /* set up the entry for the mail list */ m=new_mail(); mudtoaddr(to, temp); /* qualify local addresses */ m->to = imc_strdup(temp); sprintf(temp, "%s@%s", from, imc_name); /* qualify sender */ m->from = imc_strdup(temp); m->date = imc_strdup(date); m->subject = imc_strdup(subject); m->id = imc_strdup(generate_mailid()); m->text = imc_strdup(text); m->received = imc_now; qroot=NULL; /* initialise the local list */ to=imc_getarg(to, arg, IMC_NAME_LENGTH); while (*arg) { /* get a mudname and check if we've already added a queue entry for that * mud. If not, add it */ if (strchr(arg, '@') != NULL && (mud = imc_mudof(arg))[0] != 0 && strcasecmp(mud, imc_name)) { if (!strcmp(mud, "*")) q=NULL; /* catch the @* case - not yet implemented */ else for (q=qroot; q; q=q->next) if (!strcasecmp(q->tomud, mud)) break; if (!q) /* not seen yet */ { /* add to the top of our mini-queue */ q=new_qnode(); q->tomud=imc_strdup(mud); q->data=m; q->next=qroot; m->usage++; qroot=q; imc_add_event(when, ev_qnode_send, q, 1); when += rand()%30+30; } } /* get the next address */ to=imc_getarg(to, arg, IMC_NAME_LENGTH); } if (!qroot) /* boggle, no foreign addresses?? */ { free_mail(m); return; } /* add mail to mail list, add mini-queue to mail queue */ add_ml(m); add_mq(qroot); save_ml(); save_mq(); }