source: trunk/instant_messenger/socket/BKP_20071105/BKP_20071026/BKP_20071019/server.c @ 151

Revision 151, 12.7 KB checked in by niltonneto, 16 years ago (diff)

Commit da nova versão do módulo, usando agente em C.
Vide Página do módulo do Trac:
http://www.expressolivre.org/dev/wiki/messenger

A versão anterior encontra-se na subpasta bkp (32/64).

Line 
1#include "server.h"
2
3int main (int argc, char ** argv)
4{
5        struct in_addr remote_addr,
6                                   local_addr;
7        unsigned short remote_port,
8                                   local_port;
9        int listen_fd;
10
11        ParseArgs (argc, argv, &remote_addr, &remote_port, &local_addr, &local_port);
12
13        Initialise ();
14
15        // Create server socket before becoming a daemon so
16        // there is still a chance to print an error message.
17        listen_fd = CreateServerSocket (local_addr, local_port);
18        if ( listen_fd < 0 )
19                pbomb ("Unable to create server socket");
20
21        //Daemonise ();
22
23        MainLoop (listen_fd, remote_addr, remote_port); // never returns
24
25        exit (EXIT_SUCCESS);
26}
27
28// ParseArgs()
29// Parse the command line arguments to extract the remote
30// and local adresses and port numbers, ra, rp, la & lp.
31// Exit the program gracefully upon error.
32void ParseArgs (int argc, char ** argv, struct in_addr * ra, unsigned short * rp, struct in_addr * la, unsigned short * lp)
33{
34        // argv[0] = program name
35        // argv[1] = remote_addr
36        // argv[2] = remote_port
37        // argv[3] = local_addr (optional)
38        // argv[4] = local_port (optional)
39
40        char * p = strrchr (argv[0], '/');
41
42        strncpy (g_program_name, (p == NULL) ? argv[0] : p + 1, sizeof (g_program_name) - 1);
43
44        if ( (argc < 3) || (argc > 5) )
45        {
46                fprintf (stderr, "usage: %s remote_addr remote_port [local_addr] [local_port]\n", argv[0]);
47                exit (EXIT_FAILURE);
48        }
49
50        if ( NameToAddr (argv[1], ra) )
51                hbomb ("Unable to resolve \"%s\" to an ip address", argv[1]);
52
53        if ( NameToPort (argv[2], rp, "tcp") )
54                quit ("Unable to resolve \"%s\" to a port number", argv[2]);
55
56        if ( argc < 4 )
57                la->s_addr = htonl (INADDR_ANY);
58        else
59                if ( NameToAddr (argv[3], la) )
60                        hbomb ("Unable to resolve \"%s\" to an ip address", argv[3]);
61
62        if ( argc < 5 )
63                memcpy (lp, rp, sizeof (*lp));
64        else
65                if ( NameToPort (argv[4], lp, "tcp") )
66                        quit ("Unable to resolve \"%s\" to a port number", argv[4]);
67}
68
69// Initialise()
70// Setup syslog, signal handlers, and other intialisation.
71void Initialise (void)
72{
73        openlog (g_program_name, LOG_PID, LOG_USER);
74        syslog (LOG_INFO, "%s started", g_program_name);
75
76        chdir ("/"); // Change working directory to the root.
77
78        umask (0); // Clear our file mode creation mask
79
80        set_signal_handler (SIGCHLD, sig_child);
81
82        signal (SIGPIPE, SIG_IGN);
83}
84
85// sig_child()
86// Handles SIGCHLD from exiting child processes.
87void sig_child (int signo)
88{
89        pid_t pid;
90
91        (void) signo; // suppress compiler warning
92
93        for ( ; ; )
94        {
95                write(1, "SIG\n", sizeof("SIG\n"));
96                pid = waitpid (WAIT_ANY, NULL, WNOHANG);
97
98                if ( pid > 0 )
99                        syslog (LOG_INFO, "Caught SIGCHLD from pid %d", pid);
100                else
101                        break;
102        }
103        write(1, "SIG...\n", sizeof("SIG...\n"));
104
105        if ( (pid < 0) && (errno != ECHILD) )
106                syslog (LOG_ERR, "waitpid(): %m"), exit (EXIT_FAILURE);
107
108        return;
109}
110
111// CreateServerSocket()
112// Create a socket, bind it to the specified address
113// and port, and set it to listen for client connections.
114// Returns < 0 on failure to bind, bombs on error otherwise,
115// returns the fd of the new socket on success.
116int CreateServerSocket (struct in_addr addr, unsigned short port)
117{
118        int err,
119                fd;
120        const int on = 1;
121        struct sockaddr_in sa;
122
123        // Create a socket and get its descriptor.
124        fd = socket (AF_INET, SOCK_STREAM, 0);
125        if ( fd < 0 )
126                syslog (LOG_ERR, "socket(): %m"), exit (EXIT_FAILURE);
127
128        // Set SO_REUSEADDR socket option
129        if ( setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0 )
130                syslog (LOG_ERR, "setsockopt(fd%d, SO_REUSEADDR): %m", fd);
131
132        // Load a sa structure with the specified address and port
133        sa.sin_family = AF_INET;
134        sa.sin_port = htons (port);
135        sa.sin_addr = addr;
136        memset (sa.sin_zero, 0, sizeof (sa.sin_zero));
137
138        // Bind our socket to the address and port specified
139        err = bind (fd, (struct sockaddr *) &sa, sizeof (sa));
140        if ( err < 0 )
141        {
142                syslog (LOG_ERR, "bind(): %m");
143                return err;
144        }
145
146        // Tell socket to listen and queue up to 5 incoming connections.
147        if ( listen (fd, 5) < 0 )
148                syslog (LOG_ERR, "listen(): %m"), exit (EXIT_FAILURE);
149
150        return fd;
151}
152
153// Daemonise()
154// Put the program in the background, set PPID=1, create a
155// new session and process group, without a controlling tty.
156void Daemonise (void)
157{
158        pid_t pid;
159
160        // Close stdin, stdout & stderr
161        // TODO:
162        //              open /dev/null and dup the fd to stdin, stdout & stderr
163        //              close(STDIN_FILENO);
164        //              close(STDOUT_FILENO);
165        //              close(STDERR_FILENO);
166
167        syslog (LOG_INFO, "%s daemonising", g_program_name);
168
169        // Fork the process to put it in the background.
170
171        pid = fork ();
172        if ( pid == -1 )
173                syslog (LOG_ERR, "fork(): %m"), exit (EXIT_FAILURE);
174
175        // parent terminates here, so shell thinks the command is done.
176        if ( pid )
177                exit (0);
178
179        syslog (LOG_INFO, "%s in background", g_program_name);
180
181        // 1st child continues to run in the background with PPID=1
182
183        // Become leader of a new session and a new process group,
184        // with no controlling tty.
185        setsid ();
186
187        // Fork again to guarantee the process will
188        // not be able to aquire a controlling tty.
189
190        // signal (SIGHUP, SIG_IGN);
191        // required according to Stevens' UNP2 p333
192
193        pid = fork ();
194        if (pid == -1)
195                syslog (LOG_ERR, "fork(): %m"), exit (EXIT_FAILURE);
196
197        if ( pid ) // 1st child terminates
198                exit (0);
199
200        // 2nd child continues, no longer a session or group leader
201
202        syslog (LOG_INFO, "%s daemonised", g_program_name);
203}
204
205// MainLoop()
206// Classic concurrent server model.
207// Wait for a client to connect, fork a child process
208// to do the business with the client, parent process
209// continues to wait for the next connection.
210// This function does not return.
211void MainLoop (int listen_fd, struct in_addr rem_addr, unsigned short rem_port)
212{
213        int server_fd,
214                client_fd;
215        pid_t child_pid;
216
217        for ( ; ; )
218        {
219                client_fd = AcceptClientConnection (listen_fd);
220
221                child_pid = fork ();
222                if ( child_pid == -1 )
223                        syslog (LOG_ERR, "fork(): %m"), exit (EXIT_FAILURE);
224
225                if ( child_pid ) // parent
226                {
227                        close (client_fd);
228
229                        syslog (LOG_INFO, "Forked child pid %d to deal with client", child_pid);
230
231                        continue;
232                }
233                else // child
234                {
235                        close (listen_fd);
236
237                        server_fd = ConnectToServer (rem_addr, rem_port);
238
239                        Proxy (server_fd, client_fd);
240
241                        syslog (LOG_INFO, "exiting");
242                        _exit (0);
243                }
244        }
245}
246
247// Proxy()
248// Copies data between server_fd and client_fd in both directions
249// until one or other peer closes their connection.
250void Proxy (int server_fd, int client_fd)
251{
252        pid_t helper_pid;
253
254        syslog (LOG_INFO, "Proxy(fd%d, fd%d)", server_fd, client_fd);
255
256        signal (SIGCHLD, SIG_IGN);
257
258        helper_pid = fork ();
259        if ( helper_pid == -1 )
260                syslog (LOG_ERR, "fork(): %m"), exit (EXIT_FAILURE);
261
262        if ( helper_pid ) // parent
263        {
264                syslog (LOG_INFO, "Forked child pid %d to help with proxying", helper_pid);
265
266                Cat (server_fd, client_fd);
267
268                shutdown (client_fd, 1);
269                close (server_fd);
270                close (client_fd);
271
272                wait (0); // wait for helper to exit
273        }
274        else // child (helper)
275        {
276                Cat (client_fd, server_fd);
277
278                shutdown (server_fd, 1);
279
280                syslog (LOG_INFO, "exiting");
281                exit (0); // helper exits here
282        }
283}
284
285// AcceptClientConnection()
286// waits for a tcp connect to the socket listen_fd, which
287// must already be bound and set to listen on a local port.
288// Bombs on error, returns the fd of the new socket on success.
289int AcceptClientConnection (int listen_fd)
290{
291        int newfd;
292        struct sockaddr_in sa;
293        socklen_t socklen;
294
295        syslog (LOG_INFO, "AcceptClientConnection(fd%d)", listen_fd);
296
297        // Accept the connection and create a new socket for it.
298        socklen = sizeof (sa);
299        memset (&sa, 0, socklen);
300        do
301        {
302                newfd = accept (listen_fd, (struct sockaddr *) &sa, &socklen);
303        }
304        while ( (newfd < 0) && (errno == EINTR) );
305
306        syslog (LOG_INFO, "Accepted client connection on new socket fd%d", newfd);
307
308        if ( newfd < 0 )
309                syslog (LOG_ERR, "accept(): %m"), exit (EXIT_FAILURE);
310
311        if ( socklen != sizeof (sa) )
312                syslog (LOG_ERR, "accept() screwed up!"), exit (EXIT_FAILURE);
313
314        return (newfd);
315}
316
317// ConnectToServer()
318// attempts a tcp connect to the server specified
319// by addr and port.  Bombs on failure to connect,
320// returns the fd of the new socket on success.
321int ConnectToServer (struct in_addr addr, unsigned short port)
322{
323        // TODO: have a timeout for connect() - see Unix socket FAQ 6.2
324
325        int fd, err;
326        struct sockaddr_in sa;
327
328        // Create a socket and get its descriptor.
329        fd = socket (AF_INET, SOCK_STREAM, 0);
330
331        if ( fd < 0 )
332                syslog (LOG_ERR, "socket(): %m"), exit (EXIT_FAILURE);
333
334        sa.sin_family = AF_INET;
335        sa.sin_port = htons (port);
336        sa.sin_addr = addr;
337        memset (sa.sin_zero, 0, sizeof (sa.sin_zero));
338
339        err = connect (fd, (struct sockaddr *) &sa, sizeof (sa));
340        if (err < 0)
341        {
342                syslog (LOG_ERR, "Unable to connect socket fd%d to server: %m", fd);
343                exit (EXIT_FAILURE);
344        }
345
346        syslog (LOG_INFO, "Connected socket fd%d to server", fd);
347
348        return fd;
349}
350
351// Cat()
352// read data from in_fd and write it to out_fd until
353// the connection is closed by one of the peers.
354// Data is copied using a dynamically allocated buffer.
355void Cat (int in_fd, int out_fd)
356{
357        unsigned char * const buf = (char *) malloc (sizeof(char) * BUF_SIZE);
358        int bytes_rcvd,
359                bytes_sent = (int) NULL,
360                i;
361
362        syslog (LOG_INFO, "Cat(fd%d, fd%d)", in_fd, out_fd);
363
364        if ( buf == NULL )
365                syslog (LOG_ERR, "malloc(): %m"), exit (EXIT_FAILURE);
366
367        do
368        {
369                write(1, "DO\n", sizeof("DO\n"));
370                bzero(buf, BUF_SIZE);
371                bytes_rcvd = recv (in_fd, buf, BUF_SIZE, 0);
372
373                for ( i = 0; i < bytes_rcvd; i += bytes_sent )
374                {
375                        bytes_sent = send (out_fd, buf + i, bytes_rcvd - i, 0);
376
377                        if ( bytes_sent < 0 )
378                                break;
379                }
380                write(1, "WHILE\n", sizeof("WHILE\n"));
381        }
382        while ( (bytes_rcvd > 0) && (bytes_sent > 0) );
383       
384        write(1, "...\n", sizeof("...\n"));
385
386        if ( (bytes_rcvd < 0) && (errno != ECONNRESET) )
387                syslog (LOG_ERR, "recv(): %m"), exit (EXIT_FAILURE);
388
389        if ( (bytes_sent < 0) && (errno != EPIPE) )
390                syslog (LOG_ERR, "send(): %m"), exit (EXIT_FAILURE);
391
392        free (buf);
393}
394
395// NameToAddress()
396// Convert name to an ip address.
397// Returns 0 on success, -1 on failure.
398int NameToAddr (const char * name, struct in_addr * p_inaddr)
399{
400        struct hostent * he;
401
402        // First, attempt to convert from string ip format
403        // TODO: use inet_aton() instead
404        p_inaddr->s_addr = inet_addr (name);
405        if ( p_inaddr->s_addr != -1U ) // Success
406                return 0;
407
408        // Next, attempt to read from /etc/hosts or do a DNS lookup
409        he = gethostbyname (name);
410        if ( he != NULL ) // Success
411        {
412                memcpy (p_inaddr, he->h_addr, sizeof (struct in_addr));
413                return 0;
414        }
415
416        return -1; // Failed to resolve name to an ip address
417}
418
419// NameToPort()
420// Convert name to a port number. Name can either be a port name
421// (in which case proto must also be set to either "tcp" or "udp")
422// or name can be the ascii representation of the port number.
423// Returns 0 on success, -1 on failure.
424int NameToPort (const char * name, unsigned short * port, const char * proto)
425{
426        unsigned long lport;
427        char * errpos;
428        struct servent * se;
429
430        // First, attempt to convert string to integer
431        lport = strtoul (name, &errpos, 0);
432        if ( (*errpos == 0) && (lport <= 65535) ) // Success
433        {
434                *port = lport;
435                return 0;
436        }
437
438        // Next, attempt to read the string from /etc/services
439        se = getservbyname (name, proto);
440        if ( se != NULL) // Success
441        {
442                *port = ntohs (se->s_port);
443                return 0;
444        }
445
446        return -1; // Failed to resolve port name to a number
447}
448
449// quit()
450// Print an error message to stderr
451// and syslog, then exit the program.
452void quit (const char * fmt, ...) // quit with msg
453{
454        va_list ap;
455
456        fflush (stdout);
457
458        fprintf (stderr, "%s: ", g_program_name);
459
460        va_start (ap, fmt);
461        vfprintf (stderr, fmt, ap);
462        va_end (ap);
463
464        fputc ('\n', stderr);
465
466        syslog (LOG_ERR, "I quit!");
467
468        exit (EXIT_FAILURE);
469}
470
471// pbomb()
472// Print an error message to stderr
473// and syslog, then exit the program.
474// pbomb() additionally include the
475// string representation of errno.
476void pbomb (const char * fmt, ...) // bomb with perror
477{
478        va_list ap;
479        int errno_save = errno;
480        char buf[100];
481
482        fflush (stdout);
483
484        fprintf (stderr, "%s: ", g_program_name);
485
486        va_start (ap, fmt);
487        vsnprintf (buf, sizeof (buf), fmt, ap);
488        va_end (ap);
489
490        errno = errno_save;
491        perror (buf);
492
493        syslog (LOG_ERR, "Bang!: %s: %m", buf);
494
495        exit (EXIT_FAILURE);
496}
497
498// hbomb()
499// Print an error message to stderr
500// and syslog, then exit the program.
501// hbomb() additionally include the
502// string representation of h_errno.
503void hbomb (const char * fmt, ...) // bomb with herror
504{
505        va_list ap;
506        int h_errno_save = h_errno;
507        char buf[100];
508
509        fflush (stdout);
510
511        fprintf (stderr, "%s: ", g_program_name);
512
513        va_start (ap, fmt);
514        vsnprintf (buf, sizeof (buf), fmt, ap);
515        va_end (ap);
516
517        h_errno = h_errno_save;
518        herror (buf);
519
520        syslog (LOG_ERR, "Bang!: %s: %s", buf, hstrerror (h_errno));
521
522        exit (EXIT_FAILURE);
523}
524
525// set_signal_handler()
526// Sets a signal handler function.
527// Similar to signal() but this method
528// is more portable between platforms.
529void set_signal_handler (int signum, signal_handler_t sa_handler_func)
530{
531        struct sigaction act;
532
533        act.sa_handler = sa_handler_func;
534        sigemptyset (&(act.sa_mask));
535        act.sa_flags = 0;
536
537        if ( sigaction (signum, &act, NULL) < 0 )
538        {
539                syslog (LOG_ERR, "Error setting handler for signal %d: %m", signum);
540                exit (EXIT_FAILURE);
541        }
542}
Note: See TracBrowser for help on using the repository browser.