source: trunk/instant_messenger/socket/server.c @ 159

Revision 159, 16.8 KB checked in by niltonneto, 16 years ago (diff)
Line 
1#include "server.h"
2
3struct accept
4{
5        int newfd;
6        struct sockaddr_in sa;
7        struct in_addr jabber_addr;
8        unsigned short jabber_port;
9};
10
11struct client
12{
13        char * user;
14        int client_fd;
15        int jabber_fd;
16        int keep_alive;
17        time_t last_action;
18        struct client * next;
19        struct client * previous;
20};
21
22struct cat
23{
24        int in_fd,
25                out_fd;
26        int jabber,
27                keep_alive;
28        struct client * client;
29};
30
31struct client * clients = (struct client *)NULL;
32
33pthread_mutex_t mutexsum;
34
35int main (int argc, char ** argv)
36{
37        struct in_addr remote_addr,
38                                   local_addr;
39        unsigned short remote_port,
40                                   local_port;
41        int listen_fd;
42
43        ParseArgs (argc, argv, &remote_addr, &remote_port, &local_addr, &local_port);
44
45        Initialise ();
46
47        // Create server socket before becoming a daemon so
48        // there is still a chance to print an error message.
49        listen_fd = CreateServerSocket (local_addr, local_port);
50        if ( listen_fd < 0 )
51                pbomb ("Unable to create server socket");
52
53        //Daemonise ();
54
55        MainLoop (listen_fd, remote_addr, remote_port); // never returns
56
57        syslog (LOG_ERR, "fim MainLoop(): %m");
58        exit (EXIT_SUCCESS);
59}
60
61// ParseArgs()
62// Parse the command line arguments to extract the remote
63// and local adresses and port numbers, ra, rp, la & lp.
64// Exit the program gracefully upon error.
65void ParseArgs (int argc, char ** argv, struct in_addr * ra, unsigned short * rp, struct in_addr * la, unsigned short * lp)
66{
67        // argv[0] = program name
68        // argv[1] = remote_addr
69        // argv[2] = remote_port
70        // argv[3] = local_addr (optional)
71        // argv[4] = local_port (optional)
72
73        char * p = strrchr (argv[0], '/');
74
75        strncpy (g_program_name, (p == NULL) ? argv[0] : p + 1, sizeof (g_program_name) - 1);
76
77        if ( (argc < 3) || (argc > 5) )
78        {
79                fprintf (stderr, "usage: %s remote_addr remote_port [local_addr] [local_port]\n", argv[0]);
80                exit (EXIT_FAILURE);
81        }
82
83        if ( NameToAddr (argv[1], ra) )
84                hbomb ("Unable to resolve \"%s\" to an ip address", argv[1]);
85
86        if ( NameToPort (argv[2], rp, "tcp") )
87                quit ("Unable to resolve \"%s\" to a port number", argv[2]);
88
89        if ( argc < 4 )
90                la->s_addr = htonl (INADDR_ANY);
91        else
92                if ( NameToAddr (argv[3], la) )
93                        hbomb ("Unable to resolve \"%s\" to an ip address", argv[3]);
94
95        if ( argc < 5 )
96                memcpy (lp, rp, sizeof (*lp));
97        else
98                if ( NameToPort (argv[4], lp, "tcp") )
99                        quit ("Unable to resolve \"%s\" to a port number", argv[4]);
100}
101
102// Initialise()
103// Setup syslog, signal handlers, and other intialisation.
104void Initialise (void)
105{
106        openlog (g_program_name, LOG_PID, LOG_USER);
107        syslog (LOG_INFO, "%s started", g_program_name);
108
109        chdir ("/"); // Change working directory to the root.
110
111        umask (0); // Clear our file mode creation mask
112
113        //set_signal_handler (SIGCHLD, sig_child);
114
115        //signal (SIGPIPE, SIG_IGN);
116}
117
118// CreateServerSocket()
119// Create a socket, bind it to the specified address
120// and port, and set it to listen for client connections.
121// Returns < 0 on failure to bind, bombs on error otherwise,
122// returns the fd of the new socket on success.
123int CreateServerSocket (struct in_addr addr, unsigned short port)
124{
125        int err,
126                fd;
127        const int on = 1;
128        struct sockaddr_in sa;
129
130        // Create a socket and get its descriptor.
131        fd = socket (AF_INET, SOCK_STREAM, 0);
132        if ( fd < 0 )
133                syslog (LOG_ERR, "socket(): %m"), exit (EXIT_FAILURE);
134
135        // Set SO_REUSEADDR socket option
136        if ( setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0 )
137                syslog (LOG_ERR, "setsockopt(fd%d, SO_REUSEADDR): %m", fd);
138
139        // Load a sa structure with the specified address and port
140        sa.sin_family = AF_INET;
141        sa.sin_port = htons (port);
142        //sa.sin_addr = addr;
143        sa.sin_addr.s_addr = htonl(INADDR_ANY);
144        memset (sa.sin_zero, 0, sizeof (sa.sin_zero));
145
146        // Bind our socket to the address and port specified
147        err = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
148        if ( err < 0 )
149        {
150                syslog (LOG_ERR, "bind(): %m");
151                return err;
152        }
153
154        // Tell socket to listen and queue up to 5 incoming connections.
155        if ( listen (fd, 5) < 0 )
156                syslog (LOG_ERR, "listen(): %m"), exit (EXIT_FAILURE);
157
158        return fd;
159}
160
161// MainLoop()
162// Classic concurrent server model.
163// Wait for a client to connect, fork a child process
164// to do the business with the client, parent process
165// continues to wait for the next connection.
166// This function does not return.
167void MainLoop (int listen_fd, struct in_addr rem_addr, unsigned short rem_port)
168{
169        struct accept * accept;
170        pthread_t accept_thread,
171                          clear_list_thread;
172
173        pthread_mutex_init(&mutexsum, NULL);
174
175        pthread_create(&clear_list_thread, NULL, (void *) &ClearList, (void *)NULL );
176
177        for ( ; ; )
178        {
179                accept = AcceptClientConnection (listen_fd);
180                accept->jabber_addr = rem_addr;
181                accept->jabber_port = rem_port;
182
183                pthread_create(&accept_thread, NULL, (void *) &VerifyClient, (void *) accept);
184        }
185        pthread_mutex_destroy(&mutexsum);
186        pthread_exit(NULL);
187}
188
189void ClearList(void)
190{
191        struct client * current,
192                                  * client_remove;
193
194        for ( ; ; )
195        {
196                current = clients;
197                pthread_mutex_lock(&mutexsum);
198                while ( current != (struct client *)NULL )
199                {
200                        //printf("client: %s....\n", current->user);
201                        if ( time(NULL) - current->last_action > 60 )
202                        {
203                                //puts("\t#1 morto!");
204                                shutdown(current->jabber_fd, SHUT_RDWR);
205                                close(current->jabber_fd);
206                                current->jabber_fd = (int)NULL;
207                                //if ( current->previous == (struct client *)NULL )
208                                //{
209                                //puts("\t#2 morto!");
210                                        //if ( current->next != (struct client *)NULL )
211                                        //      current->next->previous = (struct client *)NULL;
212                                        //clients = current->next;
213                                //puts("\t#3 morto!");
214                                //}
215                                /*if ( current->previous != (struct client *)NULL )
216                                {
217                                        current->previous->next = current->next;
218                                        current->next->previous = current->previous;
219                                }
220                                else
221                                {
222                                        current->next->previous = (struct client *)NULL;
223                                }*/
224                                //puts("\t#4 morto!");
225                                //client_remove = current;
226                                current = current->next;
227                                //free(client_remove);
228                                //puts("\t#5 morto!");
229                        }
230                        else
231                                current = current->next;
232                }
233                //puts("-------------------------------");
234                pthread_mutex_unlock(&mutexsum);
235                sleep(2);
236        }
237}
238
239// AcceptClientConnection()
240// waits for a tcp connect to the socket listen_fd, which
241// must already be bound and set to listen on a local port.
242// Bombs on error, returns the fd of the new socket on success.
243struct accept * AcceptClientConnection (int listen_fd)
244{
245        int newfd;
246        struct sockaddr_in sa;
247        socklen_t socklen;
248
249        syslog (LOG_INFO, "AcceptClientConnection(fd%d)", listen_fd);
250
251        // Accept the connection and create a new socket for it.
252        socklen = sizeof (sa);
253        memset (&sa, 0, socklen);
254        do
255        {
256                newfd = accept (listen_fd, (struct sockaddr *) &sa, &socklen);
257        }
258        while ( (newfd < 0) && (errno == EINTR) );
259
260        syslog (LOG_INFO, "Accepted client connection on new socket fd%d", newfd);
261
262        if ( newfd < 0 )
263                syslog (LOG_ERR, "accept(): %m"), pthread_exit(0);
264
265        if ( socklen != sizeof (sa) )
266                syslog (LOG_ERR, "accept() screwed up!"), pthread_exit(0);
267
268        struct accept * new_accept = (struct accept *) malloc(sizeof(struct accept));
269        new_accept->newfd = newfd;
270        new_accept->sa = sa;
271
272        return (new_accept);
273}
274
275void VerifyClient(void * accept)
276{
277        int jabber_fd = (int)NULL;
278        struct accept * client = (struct accept *) accept;
279        struct client * user = (struct client *)NULL;
280        struct cat * info;
281        pthread_t cat_thread;
282       
283        FILE *file;
284        char line[80];
285        char *ips[20];
286        int cont;
287        int j;
288
289        unsigned int addr = 0;
290        char buf[250];
291        addr = ntohl(client->sa.sin_addr.s_addr);
292        sprintf(buf,"%d.%d.%d.%d",
293                        (addr >> 24)  , (addr >> 16) & 0xFF ,
294                        (addr >> 8) & 0xFF ,  addr & 0xFF);
295        printf("New connection: %s\n\n", buf);
296
297        if(!(file = fopen("/etc/socket_im/hosts","r")))
298        {
299                printf("Impossível abrir o arquivo hosts \n");
300                exit(1);
301        }
302       
303        fgets(line, 80, file);
304        while(!feof(file))
305        {
306                ips[cont] = malloc(sizeof(char)*17);
307                line[strlen(line) - 1 ] = '\0';
308                strcpy(ips[cont], line);
309                fgets(line, 80, file);
310                cont++;
311        }
312       
313        fclose(file);
314
315        for( j = 0; j < cont; j++)
316        {
317                if( strcmp( buf, ips[j] ) == 0)
318                {
319                        user = Handshake(client->newfd);
320                        jabber_fd = user->jabber_fd;
321                        break;
322                }
323        }
324
325        /*if ( strcmp(buf, "10.15.20.42") == 0 || strcmp(buf, "10.15.20.202") == 0 || strcmp(buf, "10.15.20.145") == 0 )
326        {
327                user = Handshake(client->newfd);
328                jabber_fd = user->jabber_fd;
329        }*/
330
331        if ( jabber_fd == (int)NULL )
332        {
333                jabber_fd = ConnectToServer(client->jabber_addr, client->jabber_port);
334
335                if ( user != (struct client *)NULL )
336                        user->jabber_fd = jabber_fd;
337        }
338
339        info = (struct cat *) malloc(sizeof(struct cat));
340
341        info->out_fd = jabber_fd;
342        info->in_fd = client->newfd;
343        info->jabber = (int)NULL;
344        info->keep_alive = (int)NULL;
345        info->client = user;
346
347        pthread_create(&cat_thread, NULL, (void *) &Cat, (void *)info );
348
349        pthread_exit(0);
350}
351
352struct client * Handshake(int newfd)
353{
354        float is_new_client;// = (float) NULL;
355        int bytes_rcvd,
356                bytes_sent,
357                i;
358
359        unsigned char * const buf = (char *) malloc (sizeof(char) * 5);
360        char * user = (char *) malloc(128 * sizeof(char)),
361                 * pass = (char *) malloc(128 * sizeof(char));
362
363        //struct client * clients = (struct client *)NULL;
364        struct client * new_client = (struct client *)NULL;
365
366        bzero(buf, 5);
367        sprintf(buf, "user");
368        for ( i = 0; i < sizeof(buf); i += bytes_sent )
369        {
370                bytes_sent = send (newfd, buf + i, sizeof(buf) - i, 0);
371
372                if ( bytes_sent < 0 )
373                        break;
374        }
375
376        bzero(user, 128);
377        if ( (bytes_rcvd = recv (newfd, user, 128, 0)) < 1 )
378                pthread_exit(0);
379
380        bzero(buf, 5);
381        sprintf(buf, "pass");
382        for ( i = 0; i < sizeof(buf); i += bytes_sent )
383        {
384                bytes_sent = send (newfd, buf + i, sizeof(buf) - i, 0);
385
386                if ( bytes_sent < 0 )
387                        break;
388        }
389
390        bzero(pass, 128);
391        if ( (bytes_rcvd = recv (newfd, pass, 128, 0)) < 1 )
392                pthread_exit(0);
393
394        pthread_mutex_lock (&mutexsum);
395        if ( clients == (struct client *)NULL )
396        {
397                clients = (struct client *) malloc(sizeof(struct client));
398
399                new_client = clients;
400                new_client->previous = (struct client *)NULL;
401                is_new_client = 1;
402        }
403        else
404        {
405                new_client = clients;
406                while ( ((is_new_client = (float)strcmp(new_client->user, user)) != 0) && (new_client->next != (struct client *)NULL) )
407                        new_client = new_client->next;
408
409                if ( is_new_client != 0 )
410                {
411                        new_client->next = (struct client *) malloc(sizeof(struct client));
412                        new_client->next->previous = new_client;
413                        new_client = new_client->next;
414                }
415        }
416
417        if ( is_new_client != 0 )
418        {
419                new_client->user = user;
420                new_client->jabber_fd = (int)NULL;
421                new_client->keep_alive = 1;
422                new_client->next = (struct client *)NULL;
423        }
424
425        bzero(buf, 5);
426        if ( is_new_client != 0 || new_client->jabber_fd == (int)NULL  )
427                sprintf(buf, "new");
428        else
429                sprintf(buf, "rec");
430
431        send (newfd, buf, 3, 0);
432
433        new_client->client_fd = newfd;
434        new_client->last_action = time(NULL);
435        pthread_mutex_unlock(&mutexsum);
436
437        return new_client;
438}
439
440// ConnectToServer()
441// attempts a tcp connect to the server specified
442// by addr and port.  Bombs on failure to connect,
443// returns the fd of the new socket on success.
444int ConnectToServer (struct in_addr addr, unsigned short port)
445{
446        // TODO: have a timeout for connect() - see Unix socket FAQ 6.2
447
448        int fd, err;
449        struct sockaddr_in sa;
450
451        // Create a socket and get its descriptor.
452        fd = socket (AF_INET, SOCK_STREAM, 0);
453
454        if ( fd < 0 )
455                syslog (LOG_ERR, "socket(): %m"), pthread_exit(0);//exit (EXIT_FAILURE);
456
457        sa.sin_family = AF_INET;
458        sa.sin_port = htons (port);
459        sa.sin_addr = addr;
460        memset (sa.sin_zero, 0, sizeof (sa.sin_zero));
461
462        err = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
463        if (err < 0)
464        {
465                syslog (LOG_ERR, "Unable to connect socket fd%d to server: %m", fd);
466                //exit (EXIT_FAILURE);
467                pthread_exit(0);
468        }
469
470        syslog (LOG_INFO, "Connected socket fd%d to server", fd);
471
472        return fd;
473}
474
475// Cat()
476// read data from in_fd and write it to out_fd until
477// the connection is closed by one of the peers.
478// Data is copied using a dynamically allocated buffer.
479//void Cat (int in_fd, int out_fd)
480//void Cat (int in_fd, int out_fd)
481void Cat (void * info)
482{
483        unsigned char * const buf_in = (char *) malloc (sizeof(char) * BUF_SIZE);
484        unsigned char * const buf_out = (char *) malloc (sizeof(char) * BUF_SIZE);
485        int bytes_rcvd_in = (int) NULL,
486                bytes_sent_in = (int) NULL,
487                bytes_rcvd_out = (int) NULL,
488                bytes_sent_out = (int) NULL,
489                i,
490                in_fd,
491                out_fd;
492        struct cat * user = (struct cat *) info;
493        in_fd = user->in_fd;
494        out_fd = user->out_fd;
495
496        syslog (LOG_INFO, "Cat(fd%d, fd%d)", in_fd, out_fd);
497
498        if ( buf_in == NULL || buf_out == NULL )
499                syslog (LOG_ERR, "malloc(): %m"), pthread_exit(0);
500
501        fcntl(in_fd, F_SETFL, fcntl(in_fd, F_GETFL, 0) | O_NDELAY);
502        fcntl(out_fd, F_SETFL, fcntl(out_fd, F_GETFL, 0) | O_NDELAY);
503
504        bzero(buf_in, BUF_SIZE);
505        bzero(buf_out, BUF_SIZE);
506        do
507        {
508                bzero(buf_in, BUF_SIZE);
509                if ( (bytes_rcvd_in = recv (in_fd, buf_in, BUF_SIZE, 0)) != 0 && strlen(buf_out) > 0 )
510                {
511                        for ( i = 0; i < bytes_rcvd_out; i += bytes_sent_in )
512                        {
513                                bytes_sent_in = send (in_fd, buf_out + i, bytes_rcvd_out - i, 0);
514
515                                if ( bytes_sent_in < 0 )
516                                        break;
517                        }
518                }
519
520                bzero(buf_out, BUF_SIZE);
521                if ( (bytes_rcvd_out = recv (out_fd, buf_out, BUF_SIZE, 0)) != 0 && strlen(buf_in) > 0 )
522                {
523                        //printf("\n%s\n", buf_in);
524                        for ( i = 0; i < bytes_rcvd_in; i += bytes_sent_out )
525                        {
526                                bytes_sent_out = send (out_fd, buf_in + i, bytes_rcvd_in - i, 0);
527
528                                if ( bytes_sent_out < 0 )
529                                        break;
530                        }
531                }
532                usleep(500);
533                //sleep(1);
534                //printf("bytes_rcvd_in: %d\nbytes_rcvd_out: %d\n\n", bytes_rcvd_in, bytes_rcvd_out);
535        }
536        while ( (bytes_rcvd_in != 0) && (bytes_rcvd_out != 0) );
537
538        //puts("FIM CAT");
539
540        /*
541        if ( ((bytes_rcvd_in < 0) || (bytes_rcvd_out < 0)) && (errno != ECONNRESET) )
542                syslog (LOG_ERR, "recv(): %m"), pthread_exit(0);
543
544        if ( ((bytes_sent_in < 0) || (bytes_sent_out < 0)) && (errno != EPIPE) )
545                syslog (LOG_ERR, "send(): %m"), pthread_exit(0);
546        */
547
548        if ( user->jabber && !user->keep_alive )
549        {
550                shutdown(user->jabber, SHUT_RDWR);
551                close(user->jabber);
552        }
553        //puts("shutdown");
554        shutdown(user->in_fd, SHUT_RDWR);
555        //puts("close");
556        close(user->in_fd);
557        free (buf_in);
558        //free (buf_out);
559        pthread_exit(0);
560}
561
562// NameToAddress()
563// Convert name to an ip address.
564// Returns 0 on success, -1 on failure.
565int NameToAddr (const char * name, struct in_addr * p_inaddr)
566{
567        struct hostent * he;
568
569        // First, attempt to convert from string ip format
570        // TODO: use inet_aton() instead
571        p_inaddr->s_addr = inet_addr (name);
572        if ( p_inaddr->s_addr != -1U ) // Success
573                return 0;
574
575        // Next, attempt to read from /etc/hosts or do a DNS lookup
576        he = gethostbyname (name);
577        if ( he != NULL ) // Success
578        {
579                memcpy (p_inaddr, he->h_addr, sizeof (struct in_addr));
580                return 0;
581        }
582
583        return -1; // Failed to resolve name to an ip address
584}
585
586// NameToPort()
587// Convert name to a port number. Name can either be a port name
588// (in which case proto must also be set to either "tcp" or "udp")
589// or name can be the ascii representation of the port number.
590// Returns 0 on success, -1 on failure.
591int NameToPort (const char * name, unsigned short * port, const char * proto)
592{
593        unsigned long lport;
594        char * errpos;
595        struct servent * se;
596
597        // First, attempt to convert string to integer
598        lport = strtoul (name, &errpos, 0);
599        if ( (*errpos == 0) && (lport <= 65535) ) // Success
600        {
601                *port = lport;
602                return 0;
603        }
604
605        // Next, attempt to read the string from /etc/services
606        se = getservbyname (name, proto);
607        if ( se != NULL) // Success
608        {
609                *port = ntohs (se->s_port);
610                return 0;
611        }
612
613        return -1; // Failed to resolve port name to a number
614}
615
616// quit()
617// Print an error message to stderr
618// and syslog, then exit the program.
619void quit (const char * fmt, ...) // quit with msg
620{
621        va_list ap;
622
623        fflush (stdout);
624
625        fprintf (stderr, "%s: ", g_program_name);
626
627        va_start (ap, fmt);
628        vfprintf (stderr, fmt, ap);
629        va_end (ap);
630
631        fputc ('\n', stderr);
632
633        syslog (LOG_ERR, "I quit!");
634
635        exit (EXIT_FAILURE);
636}
637
638// pbomb()
639// Print an error message to stderr
640// and syslog, then exit the program.
641// pbomb() additionally include the
642// string representation of errno.
643void pbomb (const char * fmt, ...) // bomb with perror
644{
645        va_list ap;
646        int errno_save = errno;
647        char buf[100];
648
649        fflush (stdout);
650
651        fprintf (stderr, "%s: ", g_program_name);
652
653        va_start (ap, fmt);
654        vsnprintf (buf, sizeof (buf), fmt, ap);
655        va_end (ap);
656
657        errno = errno_save;
658        perror (buf);
659
660        syslog (LOG_ERR, "Bang!: %s: %m", buf);
661
662        exit (EXIT_FAILURE);
663}
664
665// hbomb()
666// Print an error message to stderr
667// and syslog, then exit the program.
668// hbomb() additionally include the
669// string representation of h_errno.
670void hbomb (const char * fmt, ...) // bomb with herror
671{
672        va_list ap;
673        int h_errno_save = h_errno;
674        char buf[100];
675
676        fflush (stdout);
677
678        fprintf (stderr, "%s: ", g_program_name);
679
680        va_start (ap, fmt);
681        vsnprintf (buf, sizeof (buf), fmt, ap);
682        va_end (ap);
683
684        h_errno = h_errno_save;
685        herror (buf);
686
687        syslog (LOG_ERR, "Bang!: %s: %s", buf, hstrerror (h_errno));
688
689        exit (EXIT_FAILURE);
690}
691
692// set_signal_handler()
693// Sets a signal handler function.
694// Similar to signal() but this method
695// is more portable between platforms.
696void set_signal_handler (int signum, signal_handler_t sa_handler_func)
697{
698        struct sigaction act;
699
700        act.sa_handler = sa_handler_func;
701        sigemptyset (&(act.sa_mask));
702        act.sa_flags = 0;
703
704        if ( sigaction (signum, &act, NULL) < 0 )
705        {
706                syslog (LOG_ERR, "Error setting handler for signal %d: %m", signum);
707                exit (EXIT_FAILURE);
708        }
709}
Note: See TracBrowser for help on using the repository browser.