/* $Id$ */ #include #include #include #include #include "bbs.h" extern void utmplogin(int uid, int index, const int like[MAX_FRIEND], const int hate[MAX_REJECT]); extern int genfriendlist(int uid, int index, ocfs_t *fs, int maxfs); extern void utmplogoutall(void); #ifdef UTMPLOG FILE *logfp; #endif clock_t begin_clock; time_t begin_time; int count_flooding, count_login; #ifdef NOFLOODING /* 0 ok, 1 delay action, 2 reject */ int action_frequently(int uid) { int i; time_t now = time(NULL); time_t minute = now/60; time_t hour = minute/60; static time_t flood_base_minute; static time_t flood_base_hour; static struct { unsigned short lastlogin; // truncated time_t unsigned char minute_count; unsigned char hour_count; } flooding[MAX_USERS]; if(minute!=flood_base_minute) { for(i=0; i30 || flooding[uid].hour_count>60) { count_flooding++; return 2; } flooding[uid].minute_count++; flooding[uid].hour_count++; flooding[uid].lastlogin=now; if(flooding[uid].minute_count>5 || flooding[uid].hour_count>20) { count_flooding++; return 1; } return 0; } #endif /* NOFLOODING */ void syncutmp(int cfd) { int i; int like[MAX_FRIEND]; int hate[MAX_REJECT]; #ifdef UTMPLOG int x=-1; if(logfp && ftell(logfp)> 500*(1<<20)) { fclose(logfp); logfp=NULL; } if(logfp) fwrite(&x, sizeof(x), 1, logfp); #endif printf("logout all\n"); utmplogoutall(); fprintf(stderr,"sync begin\n"); for(i=0; ievb, like, sizeof(like)); evbuffer_remove(cs->evb, hate, sizeof(hate)); #ifdef UTMPLOG if(logfp) { int x=-3; fwrite(&x, sizeof(x), 1, logfp); fwrite(&uid, sizeof(uid), 1, logfp); fwrite(&index, sizeof(index), 1, logfp); fwrite(like, sizeof(like), 1, logfp); fwrite(hate, sizeof(hate), 1, logfp); } #endif utmplogin(uid, index, like, hate); nfs=genfriendlist(uid, index, fs, MAX_FS); res=0; #ifdef NOFLOODING res=action_frequently(uid); #endif evbuffer_drain(cs->evb, 2147483647); evbuffer_add(cs->evb, &res, sizeof(res)); evbuffer_add(cs->evb, &nfs, sizeof(nfs)); evbuffer_add(cs->evb, fs, sizeof(ocfs_t) * nfs); } void showstat(void) { clock_t now_clock=clock(); time_t now_time=time(0); time_t used_time=now_time-begin_time; clock_t used_clock=now_clock-begin_clock; printf("%.24s : real %.0f cpu %.2f : %d login %d flood, %.2f login/sec, %.2f%% load.\n", ctime(&now_time), (double)used_time, (double)used_clock/CLOCKS_PER_SEC, count_login, count_flooding, (double)count_login/used_time, (double)used_clock/CLOCKS_PER_SEC/used_time*100); begin_time=now_time; begin_clock=now_clock; count_login=0; count_flooding=0; } enum { FSM_ENTER, FSM_SYNC, FSM_LOGIN, FSM_WRITEBACK, FSM_EXIT }; static int firstsync=0; struct timeval tv = {5, 0}; struct event ev; int clients = 0; #define READ_BLOCK 1024 #define MAX_CLIENTS 10 void connection_client(int cfd, short event, void *arg) { struct client_state *cs = arg; int cmd, break_out = 0; int uid = 0, index = 0; // ignore clients that timeout if (event & EV_TIMEOUT) cs->state = FSM_EXIT; if (event & EV_READ) { if (cs->state != FSM_ENTER) { if (evbuffer_read(cs->evb, cfd, READ_BLOCK) <= 0) cs->state = FSM_EXIT; } else { if (evbuffer_read(cs->evb, cfd, 4) <= 0) cs->state = FSM_EXIT; } } while (!break_out) { switch (cs->state) { case FSM_ENTER: if (EVBUFFER_LENGTH(cs->evb) < sizeof(int)) { break_out = 1; break; } evbuffer_remove(cs->evb, &cmd, sizeof(cmd)); if (cmd == -1) cs->state = FSM_SYNC; else if (cmd == -2) { fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL, 0) | O_NONBLOCK); cs->state = FSM_LOGIN; } else if (cmd >= 0) cs->state = FSM_EXIT; else { printf("unknown cmd=%d\n",cmd); cs->state = FSM_EXIT; } break; case FSM_SYNC: syncutmp(cfd); firstsync = 1; cs->state = FSM_EXIT; break; case FSM_LOGIN: if (firstsync) { if (EVBUFFER_LENGTH(cs->evb) < (2 + MAX_FRIEND + MAX_REJECT) * sizeof(int)) { break_out = 1; break; } evbuffer_remove(cs->evb, &index, sizeof(index)); evbuffer_remove(cs->evb, &uid, sizeof(uid)); if (index >= USHM_SIZE) { fprintf(stderr, "bad index=%d\n", index); cs->state = FSM_EXIT; break; } count_login++; processlogin(cs, uid, index); if (count_login >= 4000 || (time(NULL) - begin_time) > 30*60) showstat(); cs->state = FSM_WRITEBACK; break_out = 1; } else cs->state = FSM_EXIT; break; case FSM_WRITEBACK: if (event & EV_WRITE) if (evbuffer_write(cs->evb, cfd) <= 0 && EVBUFFER_LENGTH(cs->evb) > 0) break_out = 1; if (EVBUFFER_LENGTH(cs->evb) == 0) cs->state = FSM_EXIT; break; case FSM_EXIT: if (clients == MAX_CLIENTS) event_add(&ev, NULL); close(cfd); evbuffer_free(cs->evb); free(cs); clients--; return; break; } } if (cs->state == FSM_WRITEBACK) event_set(&cs->ev, cfd, EV_WRITE, (void *) connection_client, cs); event_add(&cs->ev, &tv); } void connection_accept(int fd, short event, void *arg) { struct sockaddr_in clientaddr; socklen_t len = sizeof(clientaddr); int cfd; if ((cfd = accept(fd, (struct sockaddr *)&clientaddr, &len)) < 0 ) return; struct client_state *cs = calloc(1, sizeof(struct client_state)); cs->state = FSM_ENTER; cs->evb = evbuffer_new(); event_set(&cs->ev, cfd, EV_READ, (void *) connection_client, cs); event_add(&cs->ev, &tv); clients++; if (clients >= MAX_CLIENTS) { event_del(&ev); } } int main(int argc, char *argv[]) { int ch, port = 5120, sfd; char *iface_ip = NULL; #ifdef UTMPLOG logfp = fopen("utmp.log","a"); if(logfp && ftell(logfp)> 500*(1<<20)) { fclose(logfp); logfp=NULL; } #endif Signal(SIGPIPE, SIG_IGN); while( (ch = getopt(argc, argv, "p:i:h")) != -1 ) switch( ch ){ case 'p': port = atoi(optarg); break; case 'i': iface_ip = optarg; break; case 'h': default: fprintf(stderr, "usage: utmpserver [-i interface_ip] [-p port]\n"); return 1; } if( (sfd = tobind(iface_ip, port)) < 0 ) return 1; event_init(); event_set(&ev, sfd, EV_READ | EV_PERSIST, connection_accept, &ev); event_add(&ev, NULL); event_dispatch(); return 0; }