diff options
author | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-26 13:47:01 +0800 |
---|---|---|
committer | piaip <piaip@63ad8ddf-47c3-0310-b6dd-a9e9d9715204> | 2008-03-26 13:47:01 +0800 |
commit | 2a2078146f1e079732cd1a471e064d34a4399890 (patch) | |
tree | 79317a4ab4ed6a610033ed914a2c8782c3a37790 /daemon | |
parent | b860b474520a4f30b20c829d507d60a90338aadc (diff) | |
download | pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar.gz pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar.bz2 pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar.lz pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar.xz pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.tar.zst pttbbs-2a2078146f1e079732cd1a471e064d34a4399890.zip |
- (internal) directory layout structure finetune
git-svn-id: http://opensvn.csie.org/pttbbs/trunk/pttbbs@4022 63ad8ddf-47c3-0310-b6dd-a9e9d9715204
Diffstat (limited to 'daemon')
-rw-r--r-- | daemon/utmpd/Makefile | 31 | ||||
-rw-r--r-- | daemon/utmpd/README | 1 | ||||
-rw-r--r-- | daemon/utmpd/authserver.c | 236 | ||||
-rw-r--r-- | daemon/utmpd/friend.cpp | 350 | ||||
-rw-r--r-- | daemon/utmpd/utmpserver.c | 222 | ||||
-rw-r--r-- | daemon/utmpd/utmpserver2.c | 290 | ||||
-rw-r--r-- | daemon/utmpd/utmpserver3.c | 341 | ||||
-rw-r--r-- | daemon/utmpd/utmpsync.c | 27 |
8 files changed, 1498 insertions, 0 deletions
diff --git a/daemon/utmpd/Makefile b/daemon/utmpd/Makefile new file mode 100644 index 00000000..4b4969ea --- /dev/null +++ b/daemon/utmpd/Makefile @@ -0,0 +1,31 @@ +# $Id$ +#SRCROOT= .. +SRCROOT= ../.. +.include "$(SRCROOT)/pttbbs.mk" + +PROGRAMS= utmpserver utmpsync utmpserver2 utmpserver3 authserver +UTILDIR= $(SRCROOT)/util +UTILOBJ= $(UTILDIR)/util_stuff.o $(UTILDIR)/util_var.o $(UTILDIR)/util_file.o $(UTILDIR)/util_cache.o $(UTILDIR)/util_passwd.o $(UTILDIR)/util_record.o $(UTILDIR)/util_osdep.o $(UTILDIR)/util_args.o + +all: ${PROGRAMS} + +.SUFFIXES: .c .cpp .o +.c.o: + $(CCACHE) $(CC) $(CFLAGS) -c $*.c +.cpp.o: + $(CCACHE) $(CXX) $(CFLAGS) -c $*.cpp + +utmpserver: utmpserver.o $(UTILOBJ) + ${CC} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ) +utmpserver2: utmpserver2.o friend.o $(UTILOBJ) + ${CXX} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ) friend.o +utmpserver3: utmpserver3.o friend.o $(UTILOBJ) + ${CXX} ${CFLAGS} ${LDFLAGS} -levent -o $* $*.o $(UTILOBJ) friend.o +utmpsync: utmpsync.o $(UTILOBJ) + ${CC} ${CFLAGS} ${LDFLAGS} -o $* $*.o $(UTILOBJ) + +authserver: authserver.o $(UTILOBJ) + ${CC} ${CFLAGS} ${LDFLAGS} -lcrypt -levent -o $* $> + +clean: + rm -f *~ ${PROGRAMS} friend.o utmpserver.o utmpserver2.o utmpserver3.o utmpsync.o authserver.o diff --git a/daemon/utmpd/README b/daemon/utmpd/README new file mode 100644 index 00000000..3bfeddbc --- /dev/null +++ b/daemon/utmpd/README @@ -0,0 +1 @@ +這是一個測試的東西. 除非確定你知道這個程式在幹什麼, 否則請不理會這個目錄 :P diff --git a/daemon/utmpd/authserver.c b/daemon/utmpd/authserver.c new file mode 100644 index 00000000..5fbba03a --- /dev/null +++ b/daemon/utmpd/authserver.c @@ -0,0 +1,236 @@ +/* $Id$ */ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#include <event.h> + +#include "bbs.h" + +struct timeval tv = {5, 0}; +struct event ev; +int clients = 0; + +#define READ_BLOCK 256 +#define MAX_CLIENTS 10 + +#define AUTH_PASSWDS BBSHOME "/.AUTH_PASSWDS" +#define ENTRY_SIZE 64 + +struct client_state { + struct event ev; + int state; + int uid; + int len; + int response; + struct evbuffer *evb; +}; + +enum { + FSM_ENTER, + FSM_AUTH, + FSM_SETPASSWD, + FSM_RESPOND, + FSM_EXIT +}; + +/** + * 瑼X亙蝣 + * @return 1 - 撖蝣潮航炊, 0 - 撖蝣潭迤蝣 + */ +int check_passwd(int uid, char *passwd) +{ + char buf[ENTRY_SIZE]; + int i, result = 1; + + if ((i = open(AUTH_PASSWDS, O_WRONLY)) < 0) + return result; + + if (lseek(i, uid * ENTRY_SIZE, SEEK_SET) < 0) + goto end; + + if (read(i, buf, ENTRY_SIZE) < ENTRY_SIZE) + goto end; + + if (!strcmp(buf, crypt(passwd, buf))) + result = 0; +end: + memset(buf, 0, ENTRY_SIZE); + close(i); + return result; +} + +/** + * 閮剖啣蝣 + * DES crypt 撠勗末 + */ +void set_passwd(int uid, char *passwd) +{ + char buf[ENTRY_SIZE]; + char saltc[3], c; + int i; + + i = 9 * random(); + saltc[0] = i & 077; + saltc[1] = (i >> 6) & 077; + + for (i = 0; i < 2; i++) { + c = saltc[i] + '.'; + if (c > '9') + c += 7; + if (c > 'Z') + c += 6; + saltc[i] = c; + } + saltc[2] = '\0'; + + memset(buf, 0, sizeof(buf)); + strlcpy(buf, crypt(passwd, saltc), sizeof(buf)); + + if ((i = open(AUTH_PASSWDS, O_WRONLY)) < 0) + return; + + if (lseek(i, uid * ENTRY_SIZE, SEEK_SET) < 0) + goto close; + write(i, buf, ENTRY_SIZE); +close: + close(i); +} + +void connection_client(int cfd, short event, void *arg) +{ + struct client_state *cs = arg; + int cmd; + static char buf[128]; + + // ignore clients that timeout + if (event & EV_TIMEOUT) + cs->state = FSM_EXIT; + + if (event & EV_READ) { + if (evbuffer_read(cs->evb, cfd, READ_BLOCK) < 0) + cs->state = FSM_EXIT; + } + + while (1) { + switch (cs->state) { + case FSM_ENTER: + if (EVBUFFER_LENGTH(cs->evb) < sizeof(int)) + goto break_out; + evbuffer_remove(cs->evb, &cmd, sizeof(cmd)); + cs->state = FSM_AUTH; + + case FSM_AUTH: + if (EVBUFFER_LENGTH(cs->evb) < sizeof(int) * 2) + goto break_out; + + evbuffer_remove(cs->evb, &cs->uid, sizeof(cs->uid)); + evbuffer_remove(cs->evb, &cs->len, sizeof(cs->len)); + if (EVBUFFER_LENGTH(cs->evb) < cs->len) + goto break_out; + + memset(buf, 0, sizeof(buf)); + evbuffer_remove(cs->evb, buf, cs->len); + cs->response = check_passwd(cs->uid, buf); + memset(buf, 0, sizeof(buf)); + + if (cmd == 2) + cs->state = FSM_SETPASSWD; + else { + cs->state = FSM_RESPOND; + goto break_out; + } + + case FSM_SETPASSWD: + if (EVBUFFER_LENGTH(cs->evb) < sizeof(int)) + goto break_out; + + evbuffer_remove(cs->evb, &cs->len, sizeof(cs->len)); + if (EVBUFFER_LENGTH(cs->evb) < cs->len) + goto break_out; + + memset(buf, 0, sizeof(buf)); + evbuffer_remove(cs->evb, buf, cs->len); + set_passwd(cs->uid, buf); + memset(buf, 0, sizeof(buf)); + + cs->state = FSM_RESPOND; + goto break_out; + + case FSM_RESPOND: + write(cfd, &cs->response, sizeof(cs->response)); + cs->state = FSM_EXIT; + + case FSM_EXIT: + if (clients == MAX_CLIENTS) + event_add(&ev, NULL); + close(cfd); + evbuffer_free(cs->evb); + free(cs); + clients--; + return; + } + } + +break_out: + if (cs->state == FSM_RESPOND) + 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; + + fcntl(cfd, F_SETFL, fcntl(cfd, F_GETFL, 0) | O_NONBLOCK); + + struct client_state *cs = calloc(1, sizeof(struct client_state)); + cs->state = 0; + 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 = 5121, sfd; + char *iface_ip = NULL; + + 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: authserver [-i interface_ip] [-p port]\n"); + return 1; + } + + if( (sfd = tobind(iface_ip, port)) < 0 ) + return 1; + + srandom(getpid() + time(NULL)); + event_init(); + event_set(&ev, sfd, EV_READ | EV_PERSIST, connection_accept, &ev); + event_add(&ev, NULL); + event_dispatch(); + return 0; +} diff --git a/daemon/utmpd/friend.cpp b/daemon/utmpd/friend.cpp new file mode 100644 index 00000000..e62a7198 --- /dev/null +++ b/daemon/utmpd/friend.cpp @@ -0,0 +1,350 @@ +#define NDEBUG +#include <algorithm> +#include <cassert> + +// for some constant and type +#include "bbs.h" + +/* for each login of user, + * input: my index, friend[MAX_FRIEND] of uid, reject[MAX_REJECT] of uid, + * for all my relation, + * output: his index, his uid, relation to me, relation to him + */ +/* 支 user utmp 銋憭, 券函 ref index 賣舫, 蝣箔 insert & delete O(1) */ +/* 嗆鈭 refer resource recycle */ + +typedef int Uid; +typedef int Idx; + + +struct Relation { + Uid him; + short him_offset; + + Relation(Uid _him, short _him_offset=-1) :him(_him),him_offset(_him_offset) {} +}; + +template<class T> +struct freelist { + static const int KEEP = 64; + static T* list[8][KEEP]; // 2^0~2^7 + static int tail[8]; + +#define IS_2xxN(a) (a && (a&(a-1))==0) + static T* alloc(int n) { + assert(n>0); + if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) { + int t=n; + int slot; + for(slot=0; t>1; t/=2) + slot++; + assert(0<=slot && slot<8); + if(tail[slot]) { + return list[slot][--tail[slot]]; + } + } + return (T*)malloc(sizeof(T)*n); + } + static void free(T* p, int n) { + assert(n>0); + if(n<256 && IS_2xxN(n) && sizeof(T)*n<65536) { + int t=n; + int slot; + for(slot=0; t>1; t/=2) + slot++; + assert(0<=slot && slot<8); + if(tail[slot]<KEEP) { + list[slot][tail[slot]++]=p; + return; + } + } + ::free(p); + } +}; + +template<class T> T* freelist<T>::list[8][KEEP]; +template<class T> int freelist<T>::tail[8]={0}; + +template<class T,int MIN_ROOM = 8, class S = int> +struct myvector { + // 憭扯港唾 STL vector, 雿 STL capacity 芣憓銝蝮桀 + // (敺靘潛, 隞 online friend 靘隤, capacity 銝蝮桀嗅祕瘝隞暻澆蔣) + // 甇文, pointer 64bit 璈其閬 8bytes, basic overhead 8*3 bytes, + // 雿鞈瘝暻澆之, 寧 S(int or short) 摮 size & capacity 瘥頛 + T *base; + S room, n; + + myvector() :base(0),room(0),n(0) {} + ~myvector() { + clear(); + } + S append(T data) { + if(room<n+1) + resizefor(n+1); + base[n++]=data; + return n-1; + } + void pop_back() { + assert(n>0); + n--; + resizefor(n); + } + void clear() { + n=0; + resizefor(n); + } + /* + T& operator[](int idx) { + return base[idx]; + } + */ + + void resizefor(S size) { + assert(size>=n); + if(size==0) { + if(base) freelist<T>::free(base, room); + base=0; + room=0; + } else { + S origroom=room; + if(room==0) + room=MIN_ROOM; + while(room<size) room=S(room*2); + if(size<MIN_ROOM) size=MIN_ROOM; + while(room/2>size) room=S(room/2); + if(room!=origroom || base==0) { + //base=(T*)realloc(base, sizeof(T)*room); + T* tmp=freelist<T>::alloc(room); + assert(tmp); + if(n>0) + memcpy(tmp, base, sizeof(T)*n); + if(base!=0) + freelist<T>::free(base, origroom); + base=tmp; + } + assert(base); + } + } +}; + +template<class R,class B> +struct RelationList: public myvector<Relation, 8, short> { + RelationList() :myvector<Relation, 8, short>() {} + void add(Uid me, Uid him) { + RelationList<B,R>& bl=R::backlist(him); + short me_offset=append(Relation(him)); + short him_offset=bl.append(Relation(me,me_offset)); + + setbackoffset(me_offset,him_offset); + assert(bl.base[him_offset].him==me); + assert(bl.base[him_offset].him_offset==me_offset); + } + void deleteall(Uid me) { + for(int i=0; i<n; i++) { + RelationList<B,R>& bl=R::backlist(base[i].him); + assert(bl.base[base[i].him_offset].him==me); + assert(bl.base[base[i].him_offset].him_offset==i); + bl.delete_half(base[i].him_offset); + //try_recycle(base[i].him); // dirty + } + clear(); + } + private: + void setbackoffset(short which,short offset) { + assert(0<=which && which<n); + base[which].him_offset=offset; + } + void delete_half(short offset) { + assert(0<=offset && offset<n); + if(offset<n-1) { + base[offset]=base[n-1]; + R::backlist(base[offset].him).setbackoffset(base[offset].him_offset,offset); + } + pop_back(); + } + friend class RelationList<B,R>; +}; + +struct Like; +struct Likeby; +struct Hate; +struct Hateby; +struct Like: public Relation { + Like(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} + static RelationList<Likeby,Like>& backlist(Uid him); +}; +struct Likeby: public Relation { + Likeby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} + static RelationList<Like,Likeby>& backlist(Uid him); +}; +struct Hate: public Relation { + Hate(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} + static RelationList<Hateby,Hate>& backlist(Uid him); +}; +struct Hateby: public Relation { + Hateby(Uid _him, short _him_offset=-1) :Relation(_him,_him_offset) {} + static RelationList<Hate,Hateby>& backlist(Uid him); +}; + + +struct Utmp { + Utmp() { + for(int i=0; i<USHM_SIZE; i++) + utmp[i]=-1; + } + /* + Uid& operator[](int idx) { + return utmp[idx]; + } + */ + public: + Uid utmp[USHM_SIZE]; +}; +static Utmp utmp; + +struct BBSUser { + Uid me; + int online; + /* assume utmplist is short, so just use plain vector and linear search */ + myvector<int,2> utmplist; + + RelationList<Like,Likeby> like; + RelationList<Hate,Hateby> hate; + RelationList<Likeby,Like> likeby; + RelationList<Hateby,Hate> hateby; + + BBSUser() :me(-1),online(0),utmplist(),like(),hate(),likeby(),hateby() {} + BBSUser(Uid uid) :me(uid),online(0),utmplist(),like(),hate(),likeby(),hateby() {} + + void login(int utmpidx, const Uid likehim[MAX_FRIEND], const Uid hatehim[MAX_REJECT]) { + if(online>0) { + /* multiple login 閰, 隞交敺銝甈∠ like/hate 箸 */ + like.deleteall(me); + hate.deleteall(me); + } + utmp.utmp[utmpidx]=me; + utmplist.append(utmpidx); + online++; + assert(online==utmplist.n); + for(int i=0; i<MAX_FRIEND && likehim[i]; i++) + like.add(me, likehim[i]); + for(int i=0; i<MAX_REJECT && hatehim[i]; i++) + hate.add(me, hatehim[i]); + } + + void logout(int utmpidx) { + assert(utmp.utmp[utmpidx]==me); + assert(online==utmplist.n); + for(int i=0; i<utmplist.n; i++) + if(utmplist.base[i]==utmpidx) { + utmplist.base[i]=utmplist.base[utmplist.n-1]; + utmplist.pop_back(); + break; + } + utmp.utmp[utmpidx]=-1; + online--; + assert(online==utmplist.n); + if(online==0) { + like.deleteall(me); + hate.deleteall(me); + } + } + bool isfree() const { + return online==0 && like.n==0 && hate.n==0 && likeby.n==0 && hateby.n==0; + } +}; + +struct UserList { + BBSUser users[MAX_USERS]; + + UserList() { + for(int i=0; i<MAX_USERS; i++) + users[i].me=i; + } + void login(Uid uid, Idx idx, const Uid likehim[MAX_FRIEND], const Uid hatehim[MAX_REJECT]) { + assert(uid<MAX_USERS); + assert(idx<USHM_SIZE); + /* 望潔嗅 logout event, 甇 logout 芰潛 utmp override */ + if(utmp.utmp[idx]!=-1) users[utmp.utmp[idx]].logout(idx); + users[uid].login(idx, likehim, hatehim); + } +}; + +struct UserList userlist; +RelationList<Likeby,Like>& Like::backlist(Uid him) { return userlist.users[him].likeby; } +RelationList<Like,Likeby>& Likeby::backlist(Uid him) { return userlist.users[him].like; } +RelationList<Hateby,Hate>& Hate::backlist(Uid him) { return userlist.users[him].hateby; } +RelationList<Hate,Hateby>& Hateby::backlist(Uid him) { return userlist.users[him].hate; } + +struct Result { + Uid who; + int bits; + Result(Uid _who, int _bits) :who(_who),bits(_bits) {} + bool operator<(const Result& b) const { + return who<b.who; + } +}; + +int reverse_friend_stat(int stat) +{ + int stat1 = 0; + if (stat & IFH) stat1 |= HFM; + if (stat & IRH) stat1 |= HRM; + if (stat & HFM) stat1 |= IFH; + if (stat & HRM) stat1 |= IRH; + return stat1; +} + +extern "C" void utmplogin(int uid, int index, const int like[MAX_FRIEND], const int hate[MAX_REJECT]) +{ + /* login */ + userlist.login(uid, index, like, hate); +} + +extern "C" int genfriendlist(int uid, int index, ocfs_t *fs, int maxfs) +{ + /* collect data */ + BBSUser& u=userlist.users[uid]; + myvector<Result,64,short> work; + for(int i=0; i<u.like.n; i++) work.append(Result(u.like.base[i].him, IFH)); + for(int i=0; i<u.hate.n; i++) work.append(Result(u.hate.base[i].him, IRH)); + for(int i=0; i<u.likeby.n; i++) work.append(Result(u.likeby.base[i].him, HFM)); + for(int i=0; i<u.hateby.n; i++) work.append(Result(u.hateby.base[i].him, HRM)); + + /* sort */ + std::sort(work.base, work.base+work.n); + /* merge */ + if(work.n>0) { + int newn=1; + for(int i=1; i<work.n; i++) + if(work.base[i].who==work.base[newn-1].who) { + work.base[newn-1].bits|=work.base[i].bits; + } else { + work.base[newn++]=work.base[i]; + } + work.n=newn; + } + /* fill */ + int nfs=0; + for(int i=0; i<work.n && nfs<maxfs; i++) { + BBSUser& h=userlist.users[work.base[i].who]; + for(int j=0; j<h.utmplist.n && nfs<maxfs; j++) { + int rstat=reverse_friend_stat(work.base[i].bits); + if(h.utmplist.base[j]==index) continue; + fs[nfs].index=h.utmplist.base[j]; + fs[nfs].uid=h.me; + fs[nfs].friendstat=(work.base[i].bits<<24)|h.utmplist.base[j]; + fs[nfs].rfriendstat=(rstat<<24)|index; + nfs++; + } + } + return nfs; +} + +extern "C" void utmplogoutall(void) +{ + for(int i=0; i<USHM_SIZE; i++) + if(utmp.utmp[i]!=-1) + userlist.users[utmp.utmp[i]].logout(i); +} + diff --git a/daemon/utmpd/utmpserver.c b/daemon/utmpd/utmpserver.c new file mode 100644 index 00000000..85adf8c3 --- /dev/null +++ b/daemon/utmpd/utmpserver.c @@ -0,0 +1,222 @@ +/* $Id$ */ +#include "bbs.h" +#include <err.h> + +struct { + int uid; + int nFriends, nRejects; + int friend[MAX_FRIEND]; + int reject[MAX_REJECT]; +} utmp[USHM_SIZE]; + +#ifdef NOFLOODING +#define MAXWAIT 1024 +#define FLUSHTIME (3600*6) + +struct { + time_t lasttime; + int count; +} flooding[MAX_USERS]; + +int nWaits, lastflushtime; +struct { + int uid; + int fd; + int index; +} waitqueue[MAXWAIT]; +#endif /* NOFLOODING */ + +inline int countarray(int *s, int max) +{ + int i; + for( i = 0 ; i < max && s[i] ; ++i ) + ; + return i; +} + +int +reverse_friend_stat(int stat) +{ + int stat1 = 0; + if (stat & IFH) + stat1 |= HFM; + if (stat & IRH) + stat1 |= HRM; + if (stat & HFM) + stat1 |= IFH; + if (stat & HRM) + stat1 |= IRH; + if (stat & IBH) + stat1 |= IBH; + return stat1; +} + +int set_friend_bit(int me, int ui) +{ + int hit = 0; + /* 判斷對方是否為我的朋友 ? */ + if( intbsearch(utmp[ui].uid, utmp[me].friend, utmp[me].nFriends) ) + hit = IFH; + + /* 判斷我是否為對方的朋友 ? */ + if( intbsearch(utmp[me].uid, utmp[ui].friend, utmp[ui].nFriends) ) + hit |= HFM; + + /* 判斷對方是否為我的仇人 ? */ + if( intbsearch(utmp[ui].uid, utmp[me].reject, utmp[me].nRejects) ) + hit |= IRH; + + /* 判斷我是否為對方的仇人 ? */ + if( intbsearch(utmp[me].uid, utmp[ui].reject, utmp[ui].nRejects) ) + hit |= HRM; + + return hit; +} + +void initdata(int index) +{ + utmp[index].nFriends = countarray(utmp[index].friend, MAX_FRIEND); + utmp[index].nRejects = countarray(utmp[index].reject, MAX_REJECT); + if( utmp[index].nFriends > 0 ) + qsort(utmp[index].friend, utmp[index].nFriends, + sizeof(int), qsort_intcompar); + if( utmp[index].nRejects > 0 ) + qsort(utmp[index].reject, utmp[index].nRejects, + sizeof(int), qsort_intcompar); +} + +inline void syncutmp(int cfd) +{ + int nSynced = 0, i; + for( i = 0 ; i < USHM_SIZE ; ++i, ++nSynced ) + if( toread(cfd, &utmp[i].uid, sizeof(utmp[i].uid)) > 0 && + toread(cfd, utmp[i].friend, sizeof(utmp[i].friend)) > 0 && + toread(cfd, utmp[i].reject, sizeof(utmp[i].reject)) > 0 ){ + if( utmp[i].uid ) + initdata(i); + } + else + for( ; i < USHM_SIZE ; ++i ) + utmp[i].uid = 0; + close(cfd); + fprintf(stderr, "%d users synced\n", nSynced); +} + +void processlogin(int cfd, int uid, int index) +{ + if( toread(cfd, utmp[index].friend, sizeof(utmp[index].friend)) > 0 && + toread(cfd, utmp[index].reject, sizeof(utmp[index].reject)) > 0 ){ + /* 因為 logout 的時候並不會通知 utmpserver , 可能會查到一些 + 已經 logout 的帳號。所以不能只取 MAX_FRIEND 而要多取一些 */ +#define MAX_FS (2 * MAX_FRIEND) + int iu, nFrs, stat, rstat; + ocfs_t fs[MAX_FS]; + + utmp[index].uid = uid; + initdata(index); + + for( nFrs = iu = 0 ; iu < USHM_SIZE && nFrs < MAX_FS ; ++iu ) + if( iu != index && utmp[iu].uid ){ + if( (stat = set_friend_bit(index, iu)) ){ + rstat = reverse_friend_stat(stat); + fs[nFrs].index = iu; + fs[nFrs].uid = utmp[iu].uid; + fs[nFrs].friendstat = (stat << 24) + iu; + fs[nFrs].rfriendstat = (rstat << 24) + index; + ++nFrs; + } + } + + towrite(cfd, &fs, sizeof(ocfs_t) * nFrs); + } + close(cfd); +} + +#ifdef NOFLOODING +void flushwaitqueue(void) +{ + int i; + for( i = 0 ; i < nWaits ; ++i ) + processlogin(waitqueue[i].fd, waitqueue[i].uid, waitqueue[i].index); + lastflushtime = time(NULL); + nWaits = 0; + memset(flooding, 0, sizeof(flooding)); +} +#endif + +/* XXX 具有被 DoS 的可能, 請用 firewall 之類擋起來 */ +int main(int argc, char **argv) +{ + struct sockaddr_in clientaddr; + int ch, port = 5120, sfd, cfd, len, index, uid; + char *iface_ip = NULL; + + 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; + +#ifdef NOFLOODING + lastflushtime = time(NULL); +#endif + while( 1 ){ +#ifdef NOFLOODING + if( lastflushtime < (time(NULL) - 1800) ) + flushwaitqueue(); +#endif + + len = sizeof(clientaddr); + if( (cfd = accept(sfd, (struct sockaddr *)&clientaddr, &len)) < 0 ){ + if( errno != EINTR ) + sleep(1); + continue; + } + toread(cfd, &index, sizeof(index)); + if( index == -1 ){ + syncutmp(cfd); + continue; + } + + if( toread(cfd, &uid, sizeof(uid)) > 0 ){ + if( !(0 < uid || uid > MAX_USERS) ){ /* for safety */ + close(cfd); + continue; + } + +#ifdef NOFLOODING + if( (time(NULL) - flooding[uid].lasttime) < 20 ) + ++flooding[uid].count; + if( flooding[uid].count > 10 ){ + if( nWaits == MAXWAIT ) + flushwaitqueue(); + waitqueue[nWaits].uid = uid; + waitqueue[nWaits].index = index; + waitqueue[nWaits].fd = cfd; + ++nWaits; + + continue; + } + flooding[uid].lasttime = time(NULL); +#endif + + /* cfd will be closed in processlogin() */ + processlogin(cfd, uid, index); + } else { + close(cfd); + } + } + return 0; +} diff --git a/daemon/utmpd/utmpserver2.c b/daemon/utmpd/utmpserver2.c new file mode 100644 index 00000000..12c3a299 --- /dev/null +++ b/daemon/utmpd/utmpserver2.c @@ -0,0 +1,290 @@ +/* $Id$ */ +#include <stdio.h> +#include <sys/time.h> + +#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 FAKEDATA +FILE *fp; +#endif +#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; i<MAX_USERS; i++) + flooding[i].minute_count=0; + flood_base_minute=minute; + } + if(hour!=flood_base_hour) { + for(i=0; i<MAX_USERS; i++) + flooding[i].hour_count=0; + flood_base_hour=hour; + } + + if(abs(flooding[uid].lastlogin-(unsigned short)now)<=3 || + flooding[uid].minute_count>30 || + 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; i<USHM_SIZE; i++) { + int uid; +#ifdef FAKEDATA + fread(&uid, sizeof(uid), 1, fp); + if(uid==-2) + break; + fread(like, sizeof(like), 1, fp); + fread(hate, sizeof(hate), 1, fp); +#else + if( toread(cfd, &uid, sizeof(uid)) <= 0 || + toread(cfd, like, sizeof(like)) <= 0 || + toread(cfd, hate, sizeof(hate)) <= 0) + break; +#endif +#ifdef UTMPLOG + if(logfp) { + fwrite(&uid, sizeof(uid), 1, logfp); + fwrite(like, sizeof(like), 1, logfp); + fwrite(hate, sizeof(hate), 1, logfp); + } +#endif + + if(uid != 0) + utmplogin(uid, i, like, hate); + } + if(i<USHM_SIZE) { +#ifdef UTMPLOG + int x=-2; + if(logfp) fwrite(&x, sizeof(x), 1, logfp); +#endif + } + + fprintf(stderr,"sync end\n"); +} + +void processlogin(int cfd, int uid, int index) +{ + int like[MAX_FRIEND]; + int hate[MAX_REJECT]; + /* logout 銝虫 utmpserver , 航賣亙唬鈭 + 撌脩 logout 撣唾隞乩賢芸 MAX_FRIEND 閬憭銝鈭 */ +#define MAX_FS (2 * MAX_FRIEND) + int res; + int nfs; + ocfs_t fs[MAX_FS]; + +#ifdef FAKEDATA + fread(like, sizeof(like), 1, fp); + fread(hate, sizeof(hate), 1, fp); +#else + if(toread(cfd, like, sizeof(like)) <= 0 || + toread(cfd, hate, sizeof(hate)) <= 0) + return; +#endif +#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); +#ifndef FAKEDATA + res=0; +#ifdef NOFLOODING + res=action_frequently(uid); +#endif + towrite(cfd, &res, sizeof(res)); + towrite(cfd, &nfs, sizeof(nfs)); + towrite(cfd, fs, sizeof(ocfs_t) * nfs); +#endif +} + +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; +} + +int main(int argc, char *argv[]) +{ + struct sockaddr_in clientaddr; + int ch, port = 5120, sfd, cfd, len; + char *iface_ip = NULL; + int cmd; + int uid,index; + int fail; + int firstsync=0; + +#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; + } + +#ifdef FAKEDATA + fp=fopen("utmp.data","rb"); +#else + if( (sfd = tobind(iface_ip, port)) < 0 ) + return 1; +#endif + while(1) { +#ifdef FAKEDATA + if(fread(&cmd, sizeof(cmd), 1, fp)==0) break; +#else + len = sizeof(clientaddr); + if( (cfd = accept(sfd, (struct sockaddr *)&clientaddr, &len)) < 0 ){ + if( errno != EINTR ) + sleep(1); + continue; + } + toread(cfd, &cmd, sizeof(cmd)); +#endif + + if(cmd==-1) { + syncutmp(cfd); +#ifndef FAKEDATA + close(cfd); +#endif + firstsync=1; + continue; + } + if(!firstsync) { + // don't accept client before first sync, to prevent incorrect friend data + close(cfd); + continue; + } + + fail=0; +#ifdef FAKEDATA + fread(&uid, sizeof(uid), 1, fp); + fread(&index, sizeof(index), 1, fp); +#else + if(cmd==-2) { + if(toread(cfd, &index, sizeof(index)) <= 0) + fail=1; + if(toread(cfd, &uid, sizeof(uid)) <= 0) + fail=1; + } else if(cmd>=0) { + // old client + fail=1; + } else { + printf("unknown cmd=%d\n",cmd); + } +#endif + if(index>=USHM_SIZE) { + fprintf(stderr, "bad index=%d\n",index); + fail=1; + } + + if(fail) { +#ifndef FAKEDATA + close(cfd); +#endif + continue; + } + + count_login++; + processlogin(cfd, uid, index); + if(count_login>=4000 || time(NULL)-begin_time>30*60) + showstat(); +#ifndef FAKEDATA + close(cfd); +#endif + } +#ifdef FAKEDATA + fclose(fp); +#endif + return 0; +} diff --git a/daemon/utmpd/utmpserver3.c b/daemon/utmpd/utmpserver3.c new file mode 100644 index 00000000..0e1eef02 --- /dev/null +++ b/daemon/utmpd/utmpserver3.c @@ -0,0 +1,341 @@ +/* $Id$ */ +#include <stdio.h> +#include <sys/time.h> +#include <sys/types.h> +#include <event.h> + +#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; i<MAX_USERS; i++) + flooding[i].minute_count=0; + flood_base_minute=minute; + } + if(hour!=flood_base_hour) { + for(i=0; i<MAX_USERS; i++) + flooding[i].hour_count=0; + flood_base_hour=hour; + } + + if(abs(flooding[uid].lastlogin-(unsigned short)now)<=3 || + flooding[uid].minute_count>30 || + 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; i<USHM_SIZE; i++) { + int uid; + if( toread(cfd, &uid, sizeof(uid)) <= 0 || + toread(cfd, like, sizeof(like)) <= 0 || + toread(cfd, hate, sizeof(hate)) <= 0) + break; +#ifdef UTMPLOG + if(logfp) { + fwrite(&uid, sizeof(uid), 1, logfp); + fwrite(like, sizeof(like), 1, logfp); + fwrite(hate, sizeof(hate), 1, logfp); + } +#endif + + if(uid != 0) + utmplogin(uid, i, like, hate); + } + if(i<USHM_SIZE) { +#ifdef UTMPLOG + int x=-2; + if(logfp) fwrite(&x, sizeof(x), 1, logfp); +#endif + } + + fprintf(stderr,"sync end\n"); +} + +struct client_state { + struct event ev; + int state; + struct evbuffer *evb; +}; + +void processlogin(struct client_state *cs, int uid, int index) +{ + int like[MAX_FRIEND]; + int hate[MAX_REJECT]; + /* logout 銝虫 utmpserver , 航賣亙唬鈭 + 撌脩 logout 撣唾隞乩賢芸 MAX_FRIEND 閬憭銝鈭 */ +#define MAX_FS (2 * MAX_FRIEND) + int res; + int nfs; + ocfs_t fs[MAX_FS]; + + evbuffer_remove(cs->evb, 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; +} diff --git a/daemon/utmpd/utmpsync.c b/daemon/utmpd/utmpsync.c new file mode 100644 index 00000000..69d1c623 --- /dev/null +++ b/daemon/utmpd/utmpsync.c @@ -0,0 +1,27 @@ +/* $Id$ */ +#include "bbs.h" +#include <err.h> + +extern SHM_t *SHM; + +int main(int argc, char **argv) +{ + int sfd, index, i; + attach_SHM(); + if( (sfd = toconnect(OUTTACACHEHOST, OUTTACACHEPORT)) < 0 ) { + printf("connect fail\n"); + return 1; + } + + index = -1; + towrite(sfd, &index, sizeof(index)); + for( i = 0 ; i < USHM_SIZE ; ++i ) + if( towrite(sfd, &SHM->uinfo[i].uid, sizeof(SHM->uinfo[i].uid)) < 0 || + towrite(sfd, SHM->uinfo[i].myfriend, + sizeof(SHM->uinfo[i].myfriend)) < 0 || + towrite(sfd, SHM->uinfo[i].reject, + sizeof(SHM->uinfo[i].reject)) < 0 ){ + fprintf(stderr, "sync error %d\n", i); + } + return 0; +} |