ROYALD
Description
royald est un serveur de chat (port TCP 9999) tournant sous Linux et
sous Windows.
Il fonctionne avec le client RoyalChat qui se présente
sous la forme d'une applet Java.
Utilisation
Sous Linux, l'option -d permet de transformer le process en démon.
La sortie standard est alors redirigée vers le fichier royald.log.
La capture des signaux SIGINT et SIGHUP permettent respectivement de tuer le
serveur proprement, et de forcer le seveur à relire son fichier de configuration.
Le script royalds permet de faciliter ces opérations
:
- make pour recompiler le serveur.
- start pour démarrer le serveur en mode démon.
- stop pour le tuer proprement.
- config pour lui demander de recharger son fichier de configuration.
royald.conf
Trois options sont configurables dans le fichier de configuration:
- topic : indique le message envoyé au client quand il se connecte.
- owner : propriétaire du serveur (hello RR ;-)
- max_sim : nombre maximum de connexions à partir d'une même
adresse IP.
Compilation
Win32: source Visual C++. Ne pas oublier d'inclure la librairie wsock32.lib.
Linux: gcc royald.c -DLINUX_SERVER -o royald, ou utiliser le script royalds
avec l'option "make".
/* Royal daemon: serveur pour le RoyalChat Win32: Projet Visual c++ Linux: gcc royald.c -DLINUX_SERVER -o royald */ #include <stdio.h> #include <string.h> #ifdef LINUX_SERVER #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <signal.h> #include <fcntl.h> #include <sys/time.h> #include <sys/types.h> #include <errno.h> #else #include <winsock.h> #endif /* */ #define ROYAL_PORT 9999 #define MAX_USERS 20 #define USR_FREE 0 #define USR_CONNECT 1 #define USR_LOGGED 2 #define USR_ADMIN 3 /* */ struct royal_user { int sock; char name[16]; char id[16]; struct in_addr ip_addr; char buffer[128]; short len; short state; }; /* */ int init_sockets(void); int make_server(void); int server_loop(void); int client_message(int); int user_login(int, int, char **); int broadcast_msg(int, char *); int private_msg(int, char *, char *); int users_list(int); int server_infos(int, int); int sock_send(int, char *); int free_sock(int); #ifdef LINUX_SERVER void handler_routine(int); void pid_file(void); int make_daemon(void); #else DWORD WINAPI handler_routine(DWORD); #endif int check_connect_count(unsigned long); int load_config(void); /* */ /* Connexion réseau */ int server_sock; struct royal_user users[MAX_USERS]; int fd_max; int users_count; /*fd_set fdsr;*/ /* Flags de configuration */ char daemonize=0; char verbose=0; char log=0; /* Configuration (royald.conf) */ char *topic; char server_owner[16]; int max_sim; /* Statistiques */ char *last_msg[4]; int msg_index; #ifdef LINUX_SERVER struct timeval tv_uptime; struct timeval tv_last_msg; #endif /* */ int main(int argc, char **argv) { int i; for (i=1; i<argc; i++) { if (argv[i][0]=='-') { switch (argv[i][1]) { case 'd': /* detach */ daemonize++; break; case 'l': /* log */ log++; break; case 'p': /* port du demon */ break; case 'v': /* mode bavard */ verbose++; break; default: break; } } } if ((topic=(char *)malloc(5*128))==NULL) { printf("Allocation memoire impossible\n"); return -1; } msg_index=0; for(i=1; i<5; i++) { last_msg[i-1]=topic+i*128; *last_msg[i-1]=0x00; } load_config(); init_sockets(); if (make_server()==-1) return -1; #ifdef LINUX_SERVER if (daemonize) make_daemon(); signal(SIGINT, handler_routine); signal(SIGHUP, handler_routine); gettimeofday(&tv_uptime, NULL); tv_last_msg.tv_sec=0; #else SetConsoleCtrlHandler(handler_routine, 1); #endif server_loop(); return 0; } #ifdef LINUX_SERVER /* */ void pid_file(void) { FILE *f; if ((f=fopen("royald.pid", "w"))==NULL) return; fprintf(f, "%d", getpid()); fclose(f); } /* */ int make_daemon(void) { int fd; if (fork()!=0) { close(server_sock); exit(0); } /* Le pere est maintenant init (PID=1) */ pid_file(); if ((fd=open("royald.log", O_WRONLY|O_CREAT|O_TRUNC))==-1) { printf("Impossible d'ouvrir le fichier de log\n"); exit(-1); } dup2(fd, 1); return 0; } #endif /* */ int init_sockets(void) { #ifndef LINUX_SERVER WSADATA wsadata; return (int)WSAStartup(MAKEWORD(1,1), &wsadata); #else return 0; #endif } /* */ int make_server(void) { struct sockaddr_in addr; if ((server_sock=socket(AF_INET, SOCK_STREAM, 0))==-1) { printf("Creation socket impossible\n"); return -1; } addr.sin_family=AF_INET; addr.sin_port=htons(ROYAL_PORT); addr.sin_addr.s_addr=INADDR_ANY; if (bind(server_sock, (struct sockaddr *)&addr, sizeof(struct sockaddr))==-1) { printf("Bind impossible: le port est peut etre deja utilise\n"); return -1; } listen(server_sock, 5); return 0; } /* */ int server_loop(void) { int i; int len; struct sockaddr_in client; struct hostent *host; fd_set fdsr; int fd_count; int r; if (server_sock==-1) return -1; for(i=0; i<MAX_USERS; i++) users[i].state=USR_FREE; printf("RoyalServer ready. Listenning on port %d...\n", ROYAL_PORT); fd_max=server_sock; users_count=0; /*FD_ZERO(&fdsr); FD_SET(server_sock, &fdsr);*/ for(;;) { FD_ZERO(&fdsr); FD_SET(server_sock, &fdsr); for(i=0; i<users_count; i++) /*if (users[i].state!=USR_FREE)*/ FD_SET(users[i].sock, &fdsr); while ((fd_count=select(fd_max+1, &fdsr, NULL, NULL, NULL))==-1) { if (fd_count==-1 #ifdef LINUX_SERVER && errno!=EINTR #endif ) { perror("select"); return -1; } } if (FD_ISSET(server_sock, &fdsr)) { /*for(i=0; i<MAX_USERS; i++) if (users[i].state==USR_FREE) break; */ i=users_count; if (i<MAX_USERS) { len=sizeof(struct sockaddr); if ((users[i].sock=accept(server_sock, (struct sockaddr *)&client, &len))!=-1) { if (check_connect_count(client.sin_addr.s_addr)+1>max_sim) { sock_send(i, "12\t3\r\n"); #ifdef LINUX_SERVER close(users[i].sock); #else closesocket(users[i].sock); #endif } else { if (verbose) { printf("[%d] Connexion avec %s", users[i].sock, inet_ntoa(client.sin_addr)); if ((host=gethostbyaddr((char *)&client.sin_addr, 4, AF_INET))!=NULL) printf(" (%s)", host->h_name); printf("\n"); } /*FD_SET(users[i].sock, &fdsr);*/ if (users[i].sock>fd_max) fd_max=users[i].sock; users[i].ip_addr.s_addr=client.sin_addr.s_addr; users[i].state=USR_CONNECT; *users[i].name=0x00; *users[i].id=0x00; users[i].len=0; users_count++; } } else { printf("Erreur accept()\n"); } } } for (i=0; i<users_count; i++) { if (FD_ISSET(users[i].sock, &fdsr)) { if ((r=recv(users[i].sock, users[i].buffer+users[i].len, 127-users[i].len, 0))>0) { users[i].len+=r; client_message(i); } else { free_sock(i); } } } } return 0; } /* */ int client_message(int i) { char *line, *nextl; char *elem, *nexte; char *argv[5]; int argc; int cmd; line=users[i].buffer; *(line+users[i].len)=0x00; while ((nextl=strchr(line, '\n'))!=NULL) { *nextl=0x00; if (*(nextl-1)=='\r') *(nextl-1)=0x00; #ifndef LINUX_SERVER if (verbose) printf("[%d] %s\n", users[i].sock, line); #endif elem=line-1; argc=0; do { elem++; if ((nexte=strchr(elem, '\t'))!=NULL) *nexte=0x00; argv[argc++]=elem; elem=nexte; } while (elem && argc<5); cmd=atoi(argv[0]); switch(cmd) { /* Identification */ case 10: user_login(i, argc, argv); break; /* Membres */ case 20: users_list(i); break; /* Messages publics */ case 30: if (argc!=2) break; broadcast_msg(i, argv[1]); break; /* Messages privés */ case 40: if (argc!=3) break; private_msg(i, argv[1], argv[2]); break; /* Infos sur le serveur */ case 50: if (argc!=2) break; server_infos(i, atoi(argv[1])); default: break; } line=nextl+1; } if (line!=users[i].buffer) { users[i].len=strlen(line); strcpy(users[i].buffer, line); } return 0; } /* */ int user_login(int sock, int argc, char **argv) { int i, len; char buffer[128]; if (argc!=2) return -1; if (users[sock].state>=USR_LOGGED) return -1; if (strlen(argv[1])>15) { sock_send(sock, "12\t0\r\n"); /* pseudo trop long */ return -1; } len=strlen(argv[1]); for(i=0; i<len; i++) { if (argv[1][i]>='a' && argv[1][i]<='z') continue; if (argv[1][i]>='A' && argv[1][i]<='Z') continue; if (argv[1][i]>='0' && argv[1][i]<='9') continue; if (argv[1][i]=='-' || argv[1][i]=='_') continue; /* caractères interdits */ sock_send(sock, "12\t2\r\n"); return -1; } for(i=0; i<users_count; i++) if (users[i].state>=USR_LOGGED) if (strcmp(users[i].name, argv[1])==0) { sock_send(sock, "12\t1\r\n"); /* existe deja */ return -1; } strcpy(users[sock].name, argv[1]); users[sock].state=USR_LOGGED; sprintf(buffer, "11\t%s\r\n", topic); /* ok */ sock_send(sock, buffer); i=msg_index; do { if (*last_msg[i]!=0x00) sock_send(sock, last_msg[i]); i=(i+1)%4; } while (i!=msg_index); if (verbose) printf("[%d] Login: %s\n", users[sock].sock, users[sock].name); sprintf(buffer, "22\t%s\r\n", users[sock].name); for(i=0; i<users_count; i++) if (users[i].state>=USR_LOGGED) sock_send(i, buffer); return 0; } /* */ int broadcast_msg(int sock, char *msg) { int i; //char buffer[128]; if (users[sock].state<USR_LOGGED) return -1; /* tmp */ if (strlen(msg)>100) return -1; #ifdef LINUX_SERVER gettimeofday(&tv_last_msg, NULL); #endif sprintf(last_msg[msg_index], "31\t%s\t%s\r\n", users[sock].name, msg); for(i=0; i<users_count; i++) if (users[i].state>=USR_LOGGED) sock_send(i, last_msg[msg_index]); msg_index=(msg_index+1)%4; return 0; } /* */ int private_msg(int i, char *to, char *msg) { int j; char buffer[128]; if (users[i].state<USR_LOGGED) return -1; if (strlen(msg)>100 || strlen(to)>15) return -1; sprintf(buffer, "42\t%s\t%s\r\n", users[i].name, msg); for (j=0; j<users_count; j++) { if (users[j].state>=USR_LOGGED) { if (strcmp(users[j].name, to)==0) { sock_send(j, buffer); break; } } } if (j==users_count) sprintf(buffer, "43\t%s\r\n", to); /* Déconnecté */ else sprintf(buffer, "41\t%s\t%s\r\n", to, msg); sock_send(i, buffer); return 0; } /* Envoie la liste des users logges sur le chat */ int users_list(int sock) { int i; char buffer[128]; if (users[sock].state<USR_LOGGED) return -1; sock_send(sock, "21"); for (i=0; i<users_count; i++) { if (users[i].state>=USR_LOGGED) { sprintf(buffer, "\t%s\t%s", users[i].name, inet_ntoa(users[i].ip_addr)); sock_send(sock, buffer); } } sock_send(sock, "\r\n"); return 0; } /* */ int server_infos(int i, int which) { char buffer[128]={0x00}; long h; struct timeval tv_now; if (users[i].state<USR_LOGGED) return -1; #ifdef LINUX_SERVER switch (which) { case 0: /* uptime */ gettimeofday(&tv_now, NULL); h=tv_now.tv_sec-tv_uptime.tv_sec; sprintf(buffer, "50\t0\t%d\t%d\t%d\r\n", h/86400, (h%86400)/3600 , (h%3600)/60); break; case 1: /* dernier message */ h=tv_last_msg.tv_sec%86400; /* 24*3600 */ if (h==0) strcpy(buffer, "50\t1\t?\r\n"); else sprintf(buffer, "50\t1\t%d:%02d:%02d\r\n", (h/3600)%24, (h%3600)/60, h%60); break; case 4: sprintf(buffer, "50\t4\t%s\r\n", server_owner); break; default: return -1; } #else h=0; tv_now.tv_sec=0; switch (which) { case 0: strcpy(buffer, "50\t0\t1\t2\t1\r\n"); break; case 1: strcpy(buffer, "50\t1\t1:02:03\r\n"); break; case 4: sprintf(buffer, "50\t4\t%s\r\n", server_owner); break; default: return -1; } #endif sock_send(i, buffer); return 0; } /* provisoire prevoir un skprintf() */ int sock_send(int i, char *buffer) { return send(users[i].sock, buffer, strlen(buffer), 0); } /* */ int free_sock(int i) { int j; int state; char buffer[64]; /*FD_CLR((unsigned int)users[i].sock, &fdsr);*/ #ifdef LINUX_SERVER close(users[i].sock); #else closesocket(users[i].sock); #endif state=users[i].state; sprintf(buffer, "23\t%s\r\n", users[i].name); users_count--; if (i<users_count) memcpy(&users[i], &users[users_count], sizeof(struct royal_user)); fd_max=server_sock; for(j=0; j<users_count; j++) if (users[j].sock>fd_max) fd_max=users[j].sock; if (state<USR_LOGGED) return 0; if (verbose) printf("[%d] Connexion perdue\n", users[i].sock); for(j=0; j<users_count; j++) if (users[j].state>=USR_LOGGED) sock_send(j, buffer); return 0; } #ifdef LINUX_SERVER void handler_routine(int sig) { int i; if (sig==SIGHUP) { printf("Reloading config...\n"); load_config(); signal(SIGHUP, handler_routine); return; } #else DWORD WINAPI handler_routine(DWORD sig) { int i; if (sig!=CTRL_C_EVENT) return 0; #endif /* arret immminent */ strcpy(users[0].buffer, "19\r\n"); for(i=0; i<MAX_USERS; i++) { if (users[i].state>=USR_LOGGED) sock_send(i, users[0].buffer); if (users[i].state>USR_FREE) { #ifdef LINUX_SERVER close(users[i].sock); #else closesocket(users[i].sock); #endif } } free(topic); printf("Chat over\n"); #ifdef LINUX_SERVER close(server_sock); exit(0); #else closesocket(server_sock); return 1; #endif } /* */ int check_connect_count(unsigned long ip_addr) { int i; int count=0; for (i=0; i<users_count; i++) if (users[i].ip_addr.s_addr==ip_addr) count++; return count; } /* */ int load_config(void) { FILE *conf; char buffer[128]; char *p; *topic=0x00; *server_owner=0x00; max_sim=1; if ((conf=fopen("royald.conf", "rb"))==NULL) return -1; while (fgets(buffer, 127, conf)!=NULL) { if ((p=strchr(buffer, '\n'))!=NULL) { *p=0x00; p--; if (*p=='\r') *p=0x00; } if ((p=strchr(buffer, '='))!=NULL) { *p=0x00; p++; if (strcmp(buffer, "max_sim")==0) { max_sim=atoi(p); } else if (strcmp(buffer, "owner")==0) { strncpy(server_owner, p, 15); server_owner[15]=0x00; } else if (strcmp(buffer, "topic")==0) { strncpy(topic, p, 100); topic[100]=0x00; } } } fclose(conf); return 0; }