/* * urusaiserver.c * * UDP notificator server implementing simple messaging. * * Author: Dario Rodriguez dario@softhome.net */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> #include <signal.h> #include <time.h> #include <errno.h> #define SERVERPORT 20245 #define BUFLEN 2048 #define MAXID 32 #define CLIENTBLOCK 128 #define MAXPACKETSIZE 1300 typedef struct client { struct sockaddr_in addr; time_t lastrecv; char id[MAXID]; } client; typedef struct urusai { int fd; struct sockaddr_in myaddr; int numclients; int sizeclients; client *clients; } urusai; typedef enum seenaction { seen_error=0, seen_new, seen_updated, seen_nochange } seenaction; static int signal_init(int signum, void (*fn)(int)); static void sigint(int signum); volatile int sigint_flag=0; urusai *urusai_init(void); void urusai_free(urusai *u); client *urusai_clientseen(urusai *u, char *id, struct sockaddr_in *addr, seenaction *seen); int urusai_clientdeleteold(urusai *u, int timeout); client *urusai_clientget(urusai *u, char *id); int urusai_send(urusai *u, char *outbuf, int outbuf_used, client *destclient); int msg_parse(char *inbuf, int inbuf_used,char **serial,char **origid,char **destid,char **cmd, char **contents); int msg_compose(char *outbuf, int bufsize, char *serial, char *origid, char *destid, char *cmd, char *contents); int main(int argc, char *argv[]) { char inbuf[BUFLEN],outbuf[BUFLEN];; int inbuf_used,outbuf_used; struct sockaddr_in clientaddr; socklen_t clientaddr_len; urusai *u; client *origclient, *destclient; char *serial,*origid,*destid,*cmd,*contents; seenaction seen; if((u=urusai_init())==NULL) { printf("ERROR: Couldn'n get/bind socket or insufficient memory\n"); return(1); } sigint_flag=0; signal_init(SIGINT,sigint); while(!sigint_flag) { memset(&clientaddr,0,sizeof(clientaddr)); clientaddr_len=sizeof(clientaddr); if((inbuf_used=recvfrom(u->fd,inbuf,sizeof(inbuf),0,(struct sockaddr *)&clientaddr,&clientaddr_len))==-1) continue; /* interrupted by signal */ if(msg_parse(inbuf,inbuf_used,&serial,&origid,&destid,&cmd,&contents)!=0) continue; if((origclient=urusai_clientseen(u,origid,&clientaddr,&seen))==NULL) continue; if(strcmp(cmd,"ping")==0) { outbuf_used=msg_compose(outbuf, sizeof(outbuf), serial, NULL, origid,"pong",(seen==seen_new)?"new":(seen==seen_updated)?"updated":(seen==seen_nochange)?"nochange":NULL); urusai_send(u,outbuf,outbuf_used,origclient); } else if(strcmp(cmd,"message")==0) { if((destclient=urusai_clientget(u,destid))==NULL) { outbuf_used=msg_compose(outbuf, sizeof(outbuf), serial, NULL, origid,"messageerror","destid_unknown"); urusai_send(u,outbuf,outbuf_used,origclient); } else { outbuf_used=msg_compose(outbuf, sizeof(outbuf), serial, origid, destid,"message",contents); urusai_send(u,outbuf,outbuf_used,destclient); } } else if(strcmp(cmd,"receipt")==0) { if((destclient=urusai_clientget(u,destid))==NULL) { outbuf_used=msg_compose(outbuf, sizeof(outbuf), serial, NULL, origid,"receipterror","destid_unknown"); urusai_send(u,outbuf,outbuf_used,origclient); } else { outbuf_used=msg_compose(outbuf, sizeof(outbuf), serial, origid, destid,"receipt",contents); urusai_send(u,outbuf,outbuf_used,destclient); } } } urusai_free(u),u=NULL; return(0); } static int signal_init(int signum, void (*fn)(int)) { struct sigaction sa; sa.sa_handler=fn; sigemptyset(&sa.sa_mask); sa.sa_flags=0; return(sigaction(signum,&sa,0)); } static void sigint(int signum) { sigint_flag=1; } urusai * urusai_init(void) { urusai *u; if((u=malloc(sizeof(urusai)))==NULL) return(NULL); memset(u,0,sizeof(urusai)); if((u->fd=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) { urusai_free(u),u=NULL; return(NULL); } u->myaddr.sin_family=AF_INET; u->myaddr.sin_port=htons(SERVERPORT); u->myaddr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(u->fd,(struct sockaddr *)&(u->myaddr),sizeof(u->myaddr))==-1) { urusai_free(u),u=NULL; return(NULL); } return(u); } void urusai_free(urusai *u) { if(u==NULL) return; if(u->fd!=-1) close(u->fd),u->fd=-1; if(u->clients) { free(u->clients),u->clients=NULL; u->numclients=0; u->sizeclients=0; } free(u),u=NULL; } client * urusai_clientseen(urusai *u, char *id, struct sockaddr_in *addr,seenaction *seen) { int i,ifree; client *c; if(u==NULL || id==NULL || addr==NULL || seen==NULL) { if(seen!=NULL) *seen=seen_error; return(NULL); } if(u->numclients==u->sizeclients) { client *newc; if((newc=realloc(u->clients,sizeof(client)*(u->sizeclients+CLIENTBLOCK)))==NULL) return(NULL); u->clients=newc; memset(u->clients+u->sizeclients,0,CLIENTBLOCK*sizeof(client)); u->sizeclients+=CLIENTBLOCK; } for(i=0,ifree=-1;i<u->sizeclients;i++) { c=u->clients+i; if(c->id[0]=='\0' && ifree==-1) ifree=i; if(strcmp(id,c->id)==0) break; } if(i==u->sizeclients) { if(ifree==-1) { *seen=seen_error; return(NULL); } i=ifree; c=u->clients+i; strncpy(c->id,id,sizeof(c->id)); c->id[sizeof(c->id)-1]='\0'; *seen=seen_new; } else { if(memcmp(&(c->addr),addr,sizeof(c->addr))==0) *seen=seen_nochange; else *seen=seen_updated; } c->lastrecv=time(NULL); memcpy(&(c->addr),addr,sizeof(c->addr)); return(c); } int urusai_clientdeleteold(urusai *u, int timeout) { time_t cuttime; int i; client *c; if(u==NULL) return(-1); cuttime=time(NULL)-timeout; for(i=0;i<u->sizeclients;i++) { c=u->clients+i; if(c->id[0]=='\0' || c->lastrecv>cuttime) continue; memset(c,0,sizeof(client)); } return(0); } client * urusai_clientget(urusai *u, char *id) { int i; client *c; if(u==NULL || id[0]=='\0') return(NULL); for(i=0;i<u->sizeclients;i++) { c=u->clients+i; if(strcmp(id,c->id)==0) return(c); } return(NULL); } int urusai_send(urusai *u, char *outbuf, int outbuf_used, client *destclient) { struct sockaddr_in to; if(u==NULL || outbuf==NULL || outbuf_used<=0 || destclient==NULL) return(-1); memcpy(&to,&(destclient->addr),sizeof(to)); if(sendto(u->fd,outbuf,outbuf_used,0,(struct sockaddr *)&to,sizeof(to))==-1) return(-1); return(0); } int msg_parse(char *inbuf, int inbuf_used,char **serial,char **origid,char **destid,char **cmd, char **contents) { char *ptr; if(inbuf==NULL || inbuf_used<=0) return(-1); *serial=inbuf; if((ptr=memchr(*serial,0,inbuf_used-(*serial-inbuf)))==NULL) return(-1); *origid=ptr+1; if((ptr=memchr(*origid,0,inbuf_used-(*origid-inbuf)))==NULL) return(-1); *destid=ptr+1; if((ptr=memchr(*destid,0,inbuf_used-(*destid-inbuf)))==NULL) return(-1); *cmd=ptr+1; if((ptr=memchr(*cmd,0,inbuf_used-(*cmd-inbuf)))==NULL) return(-1); *contents=ptr+1; if((ptr=memchr(*contents,0,inbuf_used-(*contents-inbuf)))==NULL) return(-1); return(0); } int msg_compose(char *outbuf, int bufsize, char *serial, char *origid, char *destid, char *cmd, char *contents) { int totallen,len; char *ptr; if(outbuf==NULL || bufsize<=0 || serial==NULL) return(-1); totallen=strlen(serial)+1+ ((origid==NULL)?0:strlen(origid))+1+ ((destid==NULL)?0:strlen(destid))+1+ ((cmd==NULL)?0:strlen(cmd))+1+ ((contents==NULL)?0:strlen(contents))+1; if(totallen>bufsize || totallen>MAXPACKETSIZE) return(-1); ptr=outbuf; len=strlen(serial); memcpy(ptr,serial,len),ptr+=len; *(ptr++)=0; len=(origid==NULL)?0:strlen(origid); memcpy(ptr,origid,len),ptr+=len; *(ptr++)=0; len=(destid==NULL)?0:strlen(destid); memcpy(ptr,destid,len),ptr+=len; *(ptr++)=0; len=(cmd==NULL)?0:strlen(cmd); memcpy(ptr,cmd,len),ptr+=len; *(ptr++)=0; len=(contents==NULL)?0:strlen(contents); memcpy(ptr,contents,len),ptr+=len; *(ptr++)=0; return(ptr-outbuf); }