/* * OTter's simple chat-server * by OrangeTide Apr 8 1997 * needs: * proper handling of disconnections. (don't write to disconnected) * parsing of user commands (/MSG /LIST /MOTD) * listonline() to be written * signal handling to properly close the sockets * Full support of the telnet protocol. (echo on/off, etc) * Username & Password database * RC file interpretor (port,motdfile,etc) * channels * buffering of display strings until user has finished typing * threading off for nameserver fetches, etc. (no-pause technology) * bugs: * some telnet clients will need to manually set themselves to linemode. * */ #include #include #include #include #include #include #include #include #include #include #include #define MAXHOSTNAMELENGTH 256 #define DEF_PORT 50000 #define MAXUSERNAMELENGTH 64 #define MAXCONNECTIONS 16 #define DEF_SYSNAME "[system]" #define DEF_LOGINNAME "[login]" #define DEF_NOIPNAME "*" #define DEF_LOGOUTSTR "disconnect: %s@%s\n" #define STDOUT 0 #define STDERR 2 #define STDIN 1 /* functions */ #define F_NORMAL 0 #define F_USERNAME 1 #define F_PASSWORD 2 #define F_TELOPT1 /* IAC recieved */ #define F_TELOPT2 /* WILL,DO,DONT,WONT recieved */ struct connection { char *username; char *hostname; int Func; /* current function */ int socketfd; FILE *sock; struct connection *next; }; /* return the FD for the port that was grabbed */ int grab_port(unsigned short int portnum) { struct hostent *hp; char myhost[MAXHOSTNAMELENGTH+1]; struct sockaddr_in sa; int s; bzero(&sa, sizeof(struct sockaddr_in)); gethostname(myhost,MAXHOSTNAMELENGTH); hp=gethostbyname(myhost); if (hp==NULL) { perror("socket build error: No Hostname"); return -1; } sa.sin_family = hp->h_addrtype; sa.sin_port = htons(portnum); if ((s=socket(AF_INET,SOCK_STREAM,0))<0) { perror("socket build error: Allocating socket"); return -1; } if (bind(s,(struct sockaddr*)&sa, sizeof sa)<0) { perror("socket build error: Binding"); close(s); return -1; } listen(s,3); printf("talking on port %d.\n",portnum); return s; } /* knocks the last char off the end of a string */ char *stripdup(const char *s) { char *sr; sr=strdup(s); if (sr[strlen(sr)-2]=='\n') sr[strlen(sr)-2]='\0'; return sr; } /* add a connection onto the END of the list */ struct connection *addconnect(struct connection *AddOn, const char* username, int fd, const char* hostname) { struct connection *Xy,*curr=AddOn; Xy=(struct connection*)malloc(sizeof(struct connection)); Xy->username=strdup(username); Xy->hostname=strdup(hostname); Xy->socketfd=fd; /* Xy->Func=F_NORMAL; */ Xy->Func=F_USERNAME; Xy->next=NULL; /* * Xy->buf=(char*)calloc(sizeof(char),1024); * Xy->buf_pos=0; */ /* slap it on the end */ if(curr!=NULL) { while(curr->next!=NULL) curr=curr->next; curr->next=Xy; } return Xy; } void delconnect(struct connection *C, struct connection *rmcon) { struct connection *curr=C,*last=NULL; while( (curr!=NULL) && (curr!=rmcon) ) { last=curr; curr=curr->next; } /* should we treat this as the top connection ? */ if (last!=NULL) { last->next=rmcon->next; } /* printf("removing %s@%s\n",rmcon->username,rmcon->hostname); */ free(rmcon->username); free(rmcon->hostname); /* free(rmcon->buf); */ /* close(rmcon->socketfd); */ fclose(rmcon->sock); free(rmcon); return; } /* writes a string to ALL conections */ void broadcaststr(C,Mask,str,p0,p1,p2,p3,p4,p5,p6,p7) struct connection *C, *Mask; char *str; void *p0,*p1,*p2,*p3,*p4,*p5,*p6,*p7; { struct connection *curr=C->next; while (curr!=NULL) { if (curr!=Mask) /* writestr(*curr,str); */ { fprintf(curr->sock,str,p0,p1,p2,p3,p4,p5,p6,p7); fflush(curr->sock); } curr=curr->next; } } void bc_usertyped(struct connection *C, struct connection *From, const char *str) { broadcaststr(C,From,"%s>%s\n",From->username,str); printf("%s>%s\n",From->username,str); } void bc_disconnect(struct connection *C,struct connection *Who) { broadcaststr(C,Who,DEF_LOGOUTSTR,Who->username,Who->hostname); } void bc_connect(struct connection *C,struct connection *Who) { broadcaststr(C,Who,"connect: %s@%s\n",Who->username,Who->hostname); } void send_telnetopt(int sockfd) { /* const char buf[] = { IAC,WILL,TELOPT_ECHO,IAC,DO,TELOPT_LINEMODE }; */ /* we need: * standard linemode * WONT ECHO (you do the echoing) */ write(sockfd,buf,sizeof(buf)); } /* sits on all ports, waiting for data & disconnections */ /* assume C != NULL */ void waitfordata(struct connection *C) { fd_set sockrd, sockex, sockwr; /* struct timeval tv; */ int topfd = 0; struct connection *curr=C; FD_ZERO(&sockrd); while(curr!=NULL) { FD_SET(curr->socketfd,&sockrd); if (curr->socketfd > topfd) topfd=curr->socketfd; curr=curr->next; } sockwr=sockex=sockrd; if (select(topfd+1,&sockrd,NULL,&sockex,NULL)>0) { struct sockaddr_in myaddr; int myaddr_len; myaddr_len = sizeof(struct sockaddr_in); curr=C->next; /* start after the bound socket */ /* connection detected */ if(FD_ISSET(C->socketfd,&sockrd)) { struct hostent *hp; int sockfd; char *thehost; struct connection *Xy; FILE *sock; sockfd=accept(C->socketfd, (struct sockaddr*)&myaddr, &myaddr_len); sock=fdopen(sockfd,"a+"); fprintf(sock,"Please wait while I resolve your hostname..."); fflush(sock); if ( (hp=gethostbyaddr((char*)&myaddr.sin_addr, sizeof(struct in_addr),AF_INET)) == NULL) { thehost=inet_ntoa(myaddr.sin_addr); } else { thehost=(char*)(hp->h_name); /* yes, I know what I am doing, I really * want to copy the pointer. because it * is going to be strdup'd anyways */ } printf("login connect: %s\n",thehost); Xy=addconnect(C, DEF_LOGINNAME, sockfd, thehost); Xy->sock=sock; /* fcntl(sockfd,F_SETFL,O_NONBLOCK); */ /* send_telnetopt(sockfd); */ fprintf(sock,"\nWelcome to OrangeTide's little paradise.\nYou "\ "are from %s\nPlease set your client to linemode."\ " (this means you Win95ers!)\nEnter your name/han"\ "dle: ",thehost); fflush(sock); } while(curr!=NULL) { if(FD_ISSET(curr->socketfd,&sockrd)) { char s[4096]; int dataread; dataread=read(curr->socketfd,s,sizeof s); s[dataread]='\0'; if(!dataread) { printf("disconnect: %s@%s\n", curr->username, curr->hostname); bc_disconnect(C,curr); delconnect(C,curr); } else { switch (curr->Func) { case F_NORMAL: bc_usertyped(C,curr,s); fprintf(curr->sock,"\n>"); fflush(curr->sock); break; case F_USERNAME: free(curr->username); curr->username=stripdup(s); curr->Func=F_NORMAL; bc_connect(C,curr); break; } } } curr=curr->next; } } } void closeall(struct connection *C) { while (C->next != NULL) delconnect(C->next,C); delconnect(C,C); } int main(int argc,char **argv) { struct connection *C=NULL; int realport=DEF_PORT; printf("%c\n",getopt(argc,argv,"hp:")); /* the first socket is _ALWAYS_ the bound socket */ C=addconnect(C,DEF_SYSNAME,grab_port(DEF_PORT),DEF_NOIPNAME); /* for(i=0;i<4;i++) */ while (1) waitfordata(C); return 0; }