- added pptp support (bug #18407)
[opensuse:smpppd.git] / smpppd / smpppd.cc
1
2
3 /*
4  *  Author: Arvin Schnell <arvin@suse.de>
5  */
6
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <getopt.h>
12 #include <signal.h>
13 #include <sys/time.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/wait.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/utsname.h>
20 #include <netinet/in.h>
21 #include <netdb.h>
22 #include <arpa/inet.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <assert.h>
26 #include <syslog.h>
27
28 #include <map>
29
30 using std::map;
31
32 #include "config.h"
33 #include "smpppd.h"
34 #include "connection-config.h"
35 #include "connection.h"
36 #include "myconfig.h"
37 #include "frontend.h"
38 #include "backend.h"
39 #include "utils.h"
40 #include "parse.h"
41 #include "defines.h"
42 #include "log.h"
43
44
45 const char* program_name;
46
47 bool detach = true;
48
49 int sockfd1 = -1;               // local frontend socket
50 int sockfd2 = -1;               // inet frontend socket
51
52 Ifcfgs ifcfgs;
53 Frontends frontends;
54
55 bool reload = false;
56
57 MyMyLog mylog;
58
59
60 void
61 MyMyLog::postadd (const string& line) const
62 {
63     frontends.append_mainlog (line);
64 }
65
66
67 void
68 startup (const map <string, string>& preferred_providers)
69 {
70     InterfaceList il;
71     il.load ();
72     if (il.empty ()) {
73         mylog.add (true, "No interface configured. Use YaST2 to configure "
74                    "your dialup connections.");
75         return;
76     }
77
78     for (unsigned int i = 0; i < il.size (); i++)
79     {
80         dprintf ("%d: %s\n", i, il[i]->filename.c_str ());
81         Ifcfg* tmp = new Ifcfg (il[i]->filename, il[i]->menuname);
82
83         tmp->providerlist.load (il[i]->mode);
84         if (tmp->providerlist.empty ())
85         {
86             mylog.addf (true, "No provider configured for interface %s. "
87                         "Use YaST2 to configure your dialup connections.",
88                         tmp->ifcfg.c_str ());
89         }
90         else
91         {
92             map <string, string>::const_iterator it =
93                 preferred_providers.find (tmp->ifcfg);
94             string prov_filename = it == preferred_providers.end () ?
95                 il[i]->dfl_provider_filename : it->second;
96
97             if (tmp->ifcfg.substr (0, 10) == "ifcfg-ippp") {
98                 string tmp1 = tmp->ifcfg.substr (6);
99                 string tmp2 = "/var/run/ipppd." + tmp1 + ".provider";
100                 string tmp3 = "";
101                 if (read_first_line_from_file (tmp2.c_str (), &tmp3))
102                     prov_filename = tmp3;
103             }
104
105             dprintf ("startup %s: %s\n", tmp->ifcfg.c_str (),
106                      prov_filename.c_str ());
107
108             string error_message;
109             tmp->config = ConnectionConfig::create (tmp->ifcfg, prov_filename,
110                                                     &error_message);
111             if (!tmp->config) {
112                 mylog.addf (true, "Loading configuration for interface %s "
113                             "failed: %s", tmp->ifcfg.c_str (),
114                             error_message.c_str ());
115             }
116
117             if (tmp->config && tmp->ifcfg.substr (0, 10) == "ifcfg-ippp")
118                 tmp->isdn = Connection::create (tmp->config, 0, 0, 0);
119         }
120
121         ifcfgs.push_back (tmp);
122     }
123
124     system ("/usr/bin/killall -q -USR1 " _SMPPPD_IFCFG);
125 }
126
127
128 void
129 sigterm (int sig)
130 {
131     dprintf ("%s\n", __PRETTY_FUNCTION__);
132
133     if (sockfd1 > -1) close (sockfd1);
134     if (sockfd2 > -1) close (sockfd2);
135
136     /* Remove old local frontend socket. */
137     if (access (FRONTEND_SOCKET, F_OK) == 0)
138         unlink (FRONTEND_SOCKET);
139
140     mylog.close ();
141
142     syslog (LOG_INFO, "terminating on signal %d", sig);
143     closelog ();
144
145     exit (EXIT_SUCCESS);
146 }
147
148
149 void
150 sighup (int sig)
151 {
152     dprintf ("%s\n", __PRETTY_FUNCTION__);
153
154     reload = true;
155
156     signal (sig, sighup);
157 }
158
159
160 bool
161 allowed_host (struct sockaddr_in* clientname)
162 {
163     u_int32_t ip = ntohl ((&clientname->sin_addr)->s_addr);
164
165     for (int i = 0; i < 4; i++) {
166         const int s = i << 3;
167         if ((((ip >> s) & 0xff) < ((myconfig.ip_min >> s) & 0xff)) ||
168             (((ip >> s) & 0xff) > ((myconfig.ip_max >> s) & 0xff)))
169             return false;
170     }
171
172     return true;
173 }
174
175
176 void
177 overview ()
178 {
179 #ifndef NDEBUG
180
181     printf ("overview:\n");
182
183     for (Ifcfgs::const_iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
184     {
185         printf ("%s:  ", (*it)->ifcfg.c_str ());
186
187         ConnectionConfig* c = (*it)->config;
188         if (!c)
189             printf ("no config  ");
190         else
191             printf ("%s  ", c->get_provider_filename ().c_str ());
192
193         Backend* b = (*it)->backend;
194         if (!b)
195             printf ("no backend");
196         else
197             printf ("%d  %lld %lld", b->get_status (), b->get_rx_bytes (),
198                     b->get_tx_bytes ());
199
200         printf ("\n");
201     }
202
203 #endif
204 }
205
206
207 void
208 backend_news (Ifcfg* ifcfg)
209 {
210     Backend* b = ifcfg->backend;
211
212     string line;
213     while (b->read_line (line)) {
214         switch (b->append_buffer (line))
215         {
216             case Backend::NEW_CONFIG: {
217                 ConnectionConfig* c = ifcfg->config;
218                 assert (ifcfg->ifcfg == b->get_ifcfg_filename ());
219                 if (c->get_provider_filename () != b->get_provider_filename ())
220                     ifcfg->select_provider (b->get_provider_filename ());
221                 overview ();
222                 frontends.write_config (*ifcfg);
223             } break;
224
225             case Backend::NEW_STATUS:
226                 overview ();
227                 frontends.write_status (*ifcfg);
228                 break;
229
230             case Backend::NEW_RXTXBYTES:
231                 overview ();
232                 frontends.write_rxtx_bytes (*ifcfg);
233                 break;
234
235             case Backend::NEW_LOG1: {
236                 vector <string> tmp = b->get_last_log ();
237                 ifcfg->log = tmp;
238                 warp (&ifcfg->log, myconfig.number_of_loglines);
239                 frontends.write_log (*ifcfg);
240             } break;
241
242             case Backend::NEW_LOG2: {
243                 vector <string> tmp = b->get_last_log ();
244                 ifcfg->log.insert (ifcfg->log.end (), tmp.begin (), tmp.end ());
245                 warp (&ifcfg->log, myconfig.number_of_loglines);
246                 frontends.append_log (*ifcfg, tmp[0]);
247             } break;
248         }
249     }
250 }
251
252
253 void
254 isdn_news (Ifcfg* ifcfg)
255 {
256     Connection* isdn = ifcfg->isdn;
257
258     isdn->timer ();
259
260     frontends.write_status (*ifcfg);
261
262     frontends.write_rxtx_bytes (*ifcfg);
263 }
264
265
266 void
267 lights_off ()
268 {
269     dprintf ("%s\n", __PRETTY_FUNCTION__);
270
271     for (Ifcfgs::iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
272     {
273         switch (myconfig.stop_on_disconnect)
274         {
275             case MyConfig::STOP_ALWAYS: {
276                 (*it)->stop ();
277             } break;
278
279             case MyConfig::STOP_NOTDEMAND: {
280                 const ConnectionConfig* c = (*it)->config;
281                 if (c && !c->is_demand ())
282                     (*it)->stop ();
283             } break;
284
285             case MyConfig::STOP_NEVER: {
286             } break;
287         }
288     }
289 }
290
291
292 void
293 loop ()
294 {
295     overview ();
296
297     // note: I rely on the fact that on linux the timeout is modified to
298     // reflect the amount of time not slept
299     struct timeval timeout;
300     timeout.tv_sec = 1;
301     timeout.tv_usec = 0;
302
303     while (true)
304     {
305         if (reload)
306         {
307             reload = false;
308             dprintf ("reloading configuration\n");
309             mylog.add (true, "Reloading configuration.");
310
311             myconfig.load ();
312
313             mylog.set_max_lines (myconfig.number_of_loglines);
314
315             map <string, string> old_providers;
316
317             for (Ifcfgs::iterator it = ifcfgs.begin ();
318                  it != ifcfgs.end (); it++)
319             {
320                 if ((*it)->config)
321                     old_providers[(*it)->ifcfg] =
322                         (*it)->config->get_provider_filename ();
323                 delete (*it);
324             }
325
326             ifcfgs.clear ();
327
328             startup (old_providers);
329
330             frontends.write_ifcfgs ();
331             for (Ifcfgs::iterator it = ifcfgs.begin ();
332                  it != ifcfgs.end (); it++) {
333                 frontends.write_status (*(*it));
334                 frontends.write_providers (*(*it));
335                 frontends.write_config (*(*it));
336             }
337         }
338
339         FDSet fdset;
340
341         if (sockfd1 > -1)
342             fdset.set (sockfd1, true, false);
343
344         if (sockfd2 > -1)
345             fdset.set (sockfd2, true, false);
346
347         ifcfgs.prepare_fdset (&fdset);
348         frontends.prepare_fdset (&fdset);
349
350         int ret = fdset.select (&timeout);
351
352         if (ret < 0) {
353             fprintf (stderr, "error: select failed, %m\n");
354             exit (EXIT_FAILURE);
355         }
356
357         if (ret == 0) {
358             dprintf ("timeout\n");
359             timeout.tv_sec = 1;
360             timeout.tv_usec = 0;
361             overview ();
362
363             // look for isdn
364             for (Ifcfgs::const_iterator it = ifcfgs.begin ();
365                  it != ifcfgs.end (); it++)
366             {
367                 if ((*it)->isdn)
368                     isdn_news ((*it));
369             }
370         }
371
372         /* Connection request on original sockets. */
373
374         for (Ifcfgs::iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
375         {
376             Ifcfg* tmp1 = (*it);
377
378             if (fdset.is_read (tmp1->sockfd))
379             {
380                 struct sockaddr_in clientname;
381                 socklen_t size = sizeof (sockaddr_in);
382                 int status = accept (tmp1->sockfd, (struct sockaddr*) &clientname,
383                                      &size);
384                 if (status < 0) {
385                     perror ("accept"); // FIXME
386                     exit (EXIT_FAILURE);
387                 }
388
389                 fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
390
391                 if (myconfig.debug)
392                     syslog (LOG_INFO, "connected on local backend socket");
393
394                 dprintf ("connected on local backend socket %s\n",
395                          tmp1->ifcfg.c_str ());
396
397                 if (tmp1->backend) // should not happen
398                     delete tmp1->backend;
399
400                 tmp1->backend = new Backend (status);
401             }
402         }
403
404         if (fdset.is_read (sockfd1))
405         {
406             struct sockaddr_in clientname;
407             socklen_t size = sizeof (sockaddr_in);
408             int status = accept (sockfd1, (struct sockaddr*) &clientname, &size);
409             if (status < 0) {
410                 perror ("accept"); // FIXME
411                 exit (EXIT_FAILURE);
412             }
413
414             fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
415
416             if (myconfig.debug)
417                 syslog (LOG_INFO, "connected on local frontend socket");
418
419             dprintf ("connected on local frontend socket\n");
420
421             Frontend* tmp = new Frontend (status, clientname);
422             frontends.push_back (tmp);
423
424             tmp->authenticated = true;
425             tmp->write_line ("SuSE Meta pppd (smpppd), Version " VERSION);
426         }
427
428         if (fdset.is_read (sockfd2))
429         {
430             struct sockaddr_in clientname;
431             socklen_t size = sizeof (sockaddr_in);
432             int status = accept (sockfd2, (struct sockaddr*) &clientname, &size);
433             if (status < 0) {
434                 perror ("accept"); // FIXME
435                 exit (EXIT_FAILURE);
436             }
437
438             fcntl (status, F_SETFD, fcntl (status, F_GETFD) | FD_CLOEXEC);
439
440             if (!allowed_host (&clientname))
441             {
442                 close (status);
443
444                 syslog (LOG_INFO, "refused connection from host %s",
445                         inet_ntoa (clientname.sin_addr));
446
447                 dprintf ("refused connection from host %s\n",
448                          inet_ntoa (clientname.sin_addr));
449             }
450             else
451             {
452                 syslog (LOG_INFO, "connected from host %s",
453                         inet_ntoa (clientname.sin_addr));
454
455                 dprintf ("connected from host %s\n",
456                          inet_ntoa (clientname.sin_addr));
457
458                 Frontend* tmp = new Frontend (status, clientname);
459                 frontends.push_back (tmp);
460
461                 if (myconfig.password.empty ()) {
462                     tmp->authenticated = true;
463                     tmp->write_line ("SuSE Meta pppd (smpppd), Version " VERSION);
464                 } else {
465                     tmp->auth.make_challenge ();
466                     tmp->authenticated = false;
467                     tmp->write_line ("challenge = %s",
468                                      tmp->auth.get_challenge ().c_str ());
469                 }
470             }
471         }
472
473         /* Handle select for all backends and frontends. */
474
475         ifcfgs.handle_fdset (&fdset);
476         frontends.handle_fdset (&fdset);
477
478         /* Handle input of backends. */
479
480         for (Ifcfgs::const_iterator it = ifcfgs.begin ();
481              it != ifcfgs.end (); it++)
482         {
483             if ((*it)->backend)
484                 backend_news ((*it));
485         }
486
487         /* Delete and remove closed backends. */
488
489         for (Ifcfgs::iterator it = ifcfgs.begin (); it != ifcfgs.end (); it++)
490         {
491             Backend* b = (*it)->backend;
492             if (b) {
493                 if (b->has_been_closed) {
494                     dprintf ("closing backend due to %s\n", b->error_occurred ?
495                              "error" : "request");
496                     delete (*it)->backend;
497                     (*it)->backend = 0;
498                 }
499             }
500         }
501
502         /* Handle input of frontends. */
503
504         for (Frontends::const_iterator it = frontends.begin ();
505              it != frontends.end (); it++) {
506             string tmp;
507             while (((*it)->read_line (tmp)))
508                 (*it)->handle_command (tmp.c_str ());
509         }
510
511         /* Delete and remove closed frontends. */
512
513         for (Frontends::iterator it = frontends.begin (); it != frontends.end (); )
514         {
515             if ((*it)->has_been_closed)
516             {
517                 dprintf ("closing frontend due to %s\n", (*it)->error_occurred ?
518                          "error" : "request");
519
520                 /* stop connections if desired. */
521                 if (!(*it)->survive_me && frontends.size () == 1)
522                     lights_off ();
523
524                 delete (*it);
525                 frontends.erase (it);
526             }
527             else
528                 it++;
529         }
530     }
531 }
532
533
534 void usage () __attribute__ ((__noreturn__));
535
536 void
537 usage ()
538 {
539     fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
540     exit (EXIT_FAILURE);
541 }
542
543
544 void version () __attribute__ ((__noreturn__));
545
546 void
547 version ()
548 {
549     printf ("smpppd %s\n", VERSION);
550     exit (EXIT_SUCCESS);
551 }
552
553
554 void help () __attribute__ ((__noreturn__));
555
556 void
557 help ()
558 {
559     printf ("SuSE's `smpppd' controls several internet connections (with the "
560             "help\nof `smpppd-ifcfg'). It is not intended for direct use. Use "
561             "`rcsmpppd'\ninstead.\n"
562             "\n");
563
564     printf ("      --detach     detach (default)\n"
565             "      --nodetach   nodetach\n"
566             "\n"
567             "  -d, --debug      turn on debugging\n"
568             "\n"
569             "      --version    show version number\n"
570             "      --help       show this help\n"
571             "\n");
572
573     printf ("See `man smpppd' for even more information.\n");
574
575     exit (EXIT_SUCCESS);
576 }
577
578
579 void
580 handle_args (int argc, char* argv[])
581 {
582     struct option long_options[] = {
583         { "detach", 0, 0, 130 },
584         { "nodetach", 0, 0, 131 },
585         { "debug", 0, 0, 'd' },
586         { "version", 0, 0, 220 },
587         { "help", 0, 0, 221 },
588         { 0, 0, 0, 0 }
589     };
590
591     while (true)
592     {
593         int c = getopt_long (argc, argv, "+d", long_options, 0);
594         if (c == -1)
595             break;
596
597         switch (c)
598         {
599             case 130:
600                 detach = true;
601                 break;
602
603             case 131:
604                 detach = false;
605                 break;
606
607             case 'd':
608                 myconfig.debug = true;
609                 break;
610
611             case 220:
612                 version ();
613
614             case 221:
615                 help ();
616
617             default:
618                 usage ();
619         }
620     }
621
622     if (optind < argc) {
623         fprintf (stderr, "%s: unrecognized option `%s'\n", program_name,
624                  argv[optind]);
625         usage ();
626     }
627 }
628
629
630 int
631 main (int argc, char* argv[])
632 {
633     program_name = argv[0];
634
635     /* handle command line arguments. */
636     handle_args (argc, argv);
637
638     /* load configuation. */
639     myconfig.load ();
640     mylog.set_max_lines (myconfig.number_of_loglines);
641
642     /* install signal handlers. */
643     signal (SIGINT, &sigterm);
644     signal (SIGTERM, &sigterm);
645     signal (SIGHUP, &sighup);
646
647     /* open syslog. */
648     openlog ("smpppd", LOG_PID, LOG_DAEMON);
649     syslog (LOG_INFO, "smpppd version " VERSION " started");
650
651     /* create the sockets in the local namespace. */
652     sockfd1 = make_local_socket (FRONTEND_SOCKET);
653
654     /* create the socket in the internet namespace. */
655     if (myconfig.open_inet_socket)
656         sockfd2 = make_inet_socket (myconfig.port, myconfig.ip_bind);
657
658     /* open log file. */
659     if (!mylog.open ("/var/log/smpppd/smpppd.log"))
660         fprintf (stderr, "error: failed to open log file %s: %m\n",
661                  "/var/log/smpppd/smpppd.log");
662
663     /* insert server with version and host into log. */
664     struct utsname utsn;
665     uname (&utsn);
666     mylog.addf (true, "SuSE Meta pppd (smpppd), Version " VERSION
667                 " on %s.%s", utsn.nodename, utsn.domainname);
668
669     /* detach */
670     if (detach)
671         daemon (0, 0);
672
673     /* startup */
674     map <string, string> dummy;
675     startup (dummy);
676
677     /* loop forever. */
678     loop ();
679 }