Advertisement
daemonio

chatserv 1.2

Dec 20th, 2012
170
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.82 KB | None | 0 0
  1. /* charserv 1.2
  2.  * by Daemonio (Marcos Paulo Ferreira)
  3.  * undefinido gmail com
  4.  *
  5.  * chatserv
  6.  * Handles incoming connections from (MAX_USERS)
  7.  * users and redirect a message from an user
  8.  * to all the others. I create this code
  9.  * to play around on those boring classes on
  10.  * college. Now they are more interesting.
  11.  *
  12.  * My first goal was to create a shell script
  13.  * that would handle all connections using
  14.  * netcat. I made the script but it seems that
  15.  * every system I put my hands in has a
  16.  * different version of nc.
  17.  * So I code this simple server in C that will
  18.  * run in all linux boxes with no problems,
  19.  * and of course, to practice my select skills. :/
  20.  *
  21.  * How to use:
  22.  * $ ./chatserv 2020
  23.  *
  24.  * Now the clients has to do:
  25.  *
  26.  * $ nc <server address> 2020
  27.  *
  28.  * To type in one terminal and see everything
  29.  * you has received in another one:
  30.  *
  31.  * $ nc -lp 2021                                  (in console 1)
  32.  * $ nc <server address> 2020 | nc localhost 2021 (in console 2)
  33.  *
  34.  * Now you will type your messages in console 2
  35.  * and see what you got in console 1.
  36.  *
  37.  * If you a programmer you can easily create
  38.  * a chat client that, for example, colorize
  39.  * a message, transfer files, change nicks, etc ...
  40.  * keep in mind that you have to create a
  41.  * small protocol to facilitate the communication
  42.  * between clients. (hey, isn't that the fun part??)
  43.  *
  44.  * Tue Oct 19 19:52:35 BRST 2010
  45.  * Tue Oct 19 22:18:10 BRST 2010
  46.  * Tue Oct 19 23:48:20 BRST 2010
  47.  * Wed Oct 20 12:18:40 BRST 2010
  48.  *
  49.  * Sun Dec 16 08:43:47 BRST 2012
  50.  * Thu Dec 20 16:54:06 BRST 2012
  51.  *
  52.  * Thu Apr  4 22:54:12 BRT 2013 - wendev, version 1.0
  53.  */
  54. #include <stdio.h>
  55. #include <string.h>
  56. #include <stdlib.h>
  57. #include <netinet/in.h>
  58. #include <sys/types.h>
  59. #include <sys/socket.h>
  60. #include <sys/times.h>
  61. #include <sys/select.h>
  62. #include <unistd.h>
  63.  
  64. #define SERVER_BUSY "Sorry. The server is busy. bye\n"
  65.  
  66. #define MAX_USERS 20
  67. #define MSG_LEN 4096
  68. #define NICK_LEN 20
  69.  
  70. struct stclient {
  71. char nick[NICK_LEN+1] ;
  72. int sockfd ;
  73. int flag_has_nick ;
  74. int flag_is_connected ;
  75. } ;
  76.  
  77. struct stclient *list_of_clients ;
  78.  
  79. int socket_listening ;
  80. /* for select() */
  81. fd_set select_set ;
  82.  
  83.  
  84. int  max_users = MAX_USERS ;
  85.  
  86. /* grows up everytime an user
  87.  * got a connection.
  88.  * this variable stores the
  89.  * current list size.*/
  90. int  list_len = 0 ;
  91.  
  92. /* the user message */
  93. char msg[MSG_LEN+1] ;
  94.  
  95. void show_help(char *name) {
  96. printf("Chatserv 1.0\n") ;
  97. printf("by Daemonio (undefinido gmail com)\n\n") ;
  98. printf("[uso] %s <port> [<max_users>]\n", name) ;
  99. }
  100.  
  101. /* clean client struct */
  102. void clean_stclient(struct stclient *c) {
  103.     c->nick[0] = 0 ; c->nick[NICK_LEN] = 0 ;
  104.     c->sockfd = -1 ;
  105.     c->flag_has_nick = 0;
  106.     c->flag_is_connected = 0;
  107. }
  108.  
  109. /* if it's possible to insert a socket in
  110.  * the list, this function returns 0 and
  111.  * 1 otherwise.
  112.  */
  113. char insert_socket_into_list(int socket) {
  114.     int i ;
  115.  
  116.     if ( list_len == max_users ) {
  117.         return 1 ;
  118.     }
  119.  
  120.     for ( i = 0; i < max_users; i++ ) {
  121.         if ( list_of_clients[i].sockfd == -1 ) {
  122.             list_of_clients[i].flag_is_connected = 1 ;
  123.             list_of_clients[i].sockfd = socket ;
  124.             list_len++ ;
  125.             break ;
  126.         }
  127.     }
  128.     return 0 ;
  129. }
  130.  
  131. /* after we got the message from the user,
  132.  * we'll send it to all the others here.
  133.  * note that an user cant send a message
  134.  * to himself (list_of_clients[i] != _sock).
  135.  */
  136. void send_message_to_all(int _sock, int flag_system_msg) {
  137.     int i ;
  138.     struct stclient from ;
  139.  
  140.     /* gets who is sending the message */
  141.     for ( i = 0; i < max_users; i++ ) {
  142.         if ( list_of_clients[i].sockfd == _sock ) {
  143.              from = list_of_clients[i] ;
  144.         }
  145.     }
  146.  
  147.     for ( i = 0; i < max_users; i++ ) {
  148.         if ( (list_of_clients[i].sockfd != -1)    &&
  149.              (list_of_clients[i].sockfd != _sock) &&
  150.              (list_of_clients[i].sockfd != socket_listening) ) {
  151.  
  152.             if(flag_system_msg == 0) {
  153.                 char newmsg[MSG_LEN+1] ;
  154.                 snprintf(newmsg, MSG_LEN, "<%s> said: %s", from.nick, msg) ;
  155.                 send(list_of_clients[i].sockfd, newmsg, strlen(newmsg), 0) ;
  156.             } else if (msg[0] != '\n') { // do not send empty msg
  157.                 send(list_of_clients[i].sockfd, msg, strlen(msg), 0) ;
  158.             }
  159.         }
  160.     }
  161. }
  162.  
  163. void send_message_to_user(int _sock) {
  164.     int i ;
  165.     struct stclient from ;
  166.     char tonick[NICK_LEN+1], *p ;
  167.  
  168.     /* gets who is sending the message */
  169.     for ( i = 0; i < max_users; i++ ) {
  170.         if ( list_of_clients[i].sockfd == _sock ) {
  171.              from = list_of_clients[i] ;
  172.         }
  173.     }
  174.  
  175.     /* destination nick */
  176.     if((p = strchr(msg, ' '))==NULL) return ; // NULL if invalid synthax
  177.     *p=0; strncpy(tonick, msg+1, NICK_LEN) ;
  178.  
  179.     /* searches for the given nick */
  180.     for ( i = 0; i < max_users; i++ ) {
  181.         if ( (list_of_clients[i].sockfd != -1)    &&
  182.              (list_of_clients[i].sockfd != _sock) &&
  183.              (list_of_clients[i].sockfd != socket_listening) &&
  184.              (strcmp(list_of_clients[i].nick, tonick)== 0) ) {
  185.  
  186.             char newmsg[MSG_LEN+1] ;
  187.             snprintf(newmsg, MSG_LEN, "<%s> said exclusively to you: %s", from.nick, p+1) ;
  188.             send(list_of_clients[i].sockfd, newmsg, strlen(newmsg), 0) ;
  189.         }
  190.     }
  191. }
  192.  
  193. /* searches for a socket in the list
  194.  * and closes it.
  195.  */
  196. void remove_socket_from_list(int _sock) {
  197.     int i ;
  198.  
  199.     for ( i = 0; i < max_users; i++ ) {
  200.         if ( list_of_clients[i].sockfd == _sock ) {
  201.             sprintf(msg, "%s has quit.\n", list_of_clients[i].nick) ;
  202.             send_message_to_all(list_of_clients[i].sockfd, 1) ;
  203.  
  204.             close(list_of_clients[i].sockfd) ;
  205.             clean_stclient(&list_of_clients[i]) ;
  206.             list_len-- ;
  207.             break ;
  208.         }
  209.     }
  210. }
  211.  
  212. /* gets the message from an user.
  213.  * this message will be in `msg'.
  214.  * Returns 0 if the message was delivered
  215.  * succesfully and 1 if the client
  216.  * has finished the connection.
  217.  */
  218. char get_message_from_socket(int _sock) {
  219.     int t ;
  220.  
  221.     memset(msg,0x0,MSG_LEN+1) ;
  222.     t = recv(_sock, msg, MSG_LEN, 0 ) ;
  223.  
  224.     if ( t == 0 ) {
  225.         remove_socket_from_list(_sock) ;
  226.         return 1 ;
  227.     }
  228.  
  229.     return 0 ;
  230. }
  231.  
  232. int main(int argc, char **argv) {
  233.     int port ;
  234.     int t    ;
  235.  
  236.     struct sockaddr_in server ;
  237.     struct timeval select_time ;
  238.  
  239.     if ( argc == 1 ) {
  240.         show_help(argv[0]) ;
  241.         return -1 ;
  242.     }
  243.  
  244.     if ( argc > 2 ) {
  245.         max_users = atoi(argv[2]) ;
  246.     }
  247.  
  248.     port = atoi(argv[1]) ;
  249.  
  250.     socket_listening = socket(AF_INET, SOCK_STREAM, 0) ;
  251.  
  252.     if ( socket_listening < 0 ) {
  253.         perror("socket") ;
  254.         return -1 ;
  255.     }
  256.  
  257.     server.sin_family = AF_INET ;
  258.     server.sin_port = htons(port) ;
  259.     server.sin_addr.s_addr = INADDR_ANY ;
  260.  
  261.     t = sizeof(struct sockaddr_in) ;
  262.     if ( bind( socket_listening, (struct sockaddr *) &server, t ) < 0 ) {
  263.         perror("bind") ;
  264.         return -1 ;
  265.     }
  266.  
  267.     if ( listen(socket_listening, 5) < 0 ) {
  268.         perror("listen") ;
  269.         return -1 ;
  270.     }
  271.  
  272.     /* create the list of sockets.
  273.      * for each client, there is a socket in this list.
  274.      */
  275.     list_of_clients = (struct stclient *) malloc( max_users * sizeof(struct stclient) ) ;
  276.     if ( list_of_clients == NULL ) {
  277.         perror("malloc") ;
  278.         return -1 ;
  279.     }
  280.  
  281.     /* "clean up" the list. */
  282.     for ( t = 0; t < max_users; t++ )
  283.         clean_stclient(&list_of_clients[t]) ;
  284.  
  285.     /* you'll need a ctrl+c to break this loop */
  286.     while ( 1 ) {
  287.         /* gets all the sockets and put in a
  288.          * fd_set struct. */
  289.         FD_ZERO(&select_set) ;
  290.         FD_SET(socket_listening, &select_set) ;
  291.         for ( t = 0; list_len > 0 && t < max_users; t++ ) {
  292.             if ( list_of_clients[t].sockfd != -1 ) {
  293.                 FD_SET(list_of_clients[t].sockfd, &select_set) ;
  294.             }
  295.         }
  296.  
  297.         printf("[+] Listening on %d [%d/%d] ...\n", port, list_len, max_users) ;
  298.  
  299.         /* select will wait 2 seconds before
  300.          * returning. */
  301.         select_time.tv_sec = 2 ;
  302.         select_time.tv_usec = 0 ;
  303.  
  304.         /* select returns:
  305.          * < 0 if error.
  306.          * = 0 if nothing happened
  307.          * > 0 number of sockets on the sets
  308.          */
  309.         if ( (t=select(FD_SETSIZE, &select_set, NULL, NULL, &select_time)) < 0 ) {
  310.             perror("select") ;
  311.             return -1 ;
  312.         }
  313.  
  314.         /* wow, we have something ... */
  315.         if ( t > 0 ) {
  316.             /* if it's the listening socket, we have to
  317.              * accept the incoming connection and add
  318.              * the new socket in the list. */
  319.             if ( FD_ISSET(socket_listening, &select_set) ) {
  320.                 int n ;
  321.  
  322.                 if ( (n=accept(socket_listening, NULL, NULL)) < 0 ) {
  323.                     perror("accept") ;
  324.                 } else if ( insert_socket_into_list(n) == 1 ) { /* server is busy */
  325.                     send(n,SERVER_BUSY,strlen(SERVER_BUSY),0) ;
  326.                     close(n) ;
  327.                 }
  328.                 continue ;
  329.             } else {
  330.                 int i ;
  331.  
  332.                 /* handle the incoming data. */
  333.                 for ( i = 0; i < max_users; i++ ) {
  334.                     if ( FD_ISSET(list_of_clients[i].sockfd, &select_set) ) {
  335.                         if ( get_message_from_socket(list_of_clients[i].sockfd) == 0 ) {
  336.                             int flag_system_msg = 0 ;
  337.  
  338.                             if(list_of_clients[i].flag_has_nick == 0) { /* setting the nickname */
  339.                                   list_of_clients[i].flag_has_nick = 1 ;
  340.                                   strncpy(list_of_clients[i].nick, msg, NICK_LEN) ;
  341.  
  342.                                   /* remove trailling '\n' */
  343.                                   if(list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=='\n') {
  344.                                         list_of_clients[i].nick[strlen(list_of_clients[i].nick)-1]=0 ;
  345.                                   }
  346.  
  347.                                   snprintf(msg, MSG_LEN, "%s : is now on the chat!\n", list_of_clients[i].nick) ;
  348.                                   /* if it is 1, doenst include the prefix "<nick> said: " on message */
  349.                                   flag_system_msg = 1 ;
  350.                             } else if(msg[0] == '@') { /* if starts with an '@', sends private msg */
  351.                                 send_message_to_user(list_of_clients[i].sockfd) ;
  352.                             } else { /* otherwise, sends msg to everybody */
  353.                                 send_message_to_all(list_of_clients[i].sockfd, flag_system_msg) ;
  354.                             }
  355.                         }
  356.                     }
  357.                 }
  358.             }
  359.         }
  360.     } /* while */
  361.  
  362.     return 0 ;
  363. } /* main */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement