source: trunk/instant_messenger/socket/BKP_20071105/BKP_20071026/BKP_20071019/BKP_20071018/BKP_20071009/BKP_20071008/BKP/BKP/proxy.c @ 151

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