source: trunk/instant_messenger/socket/BKP_20071105/server_default.c @ 151

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