131921Sbrian/*-
231921Sbrian * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
331921Sbrian * All rights reserved.
431921Sbrian *
531921Sbrian * Redistribution and use in source and binary forms, with or without
631921Sbrian * modification, are permitted provided that the following conditions
731921Sbrian * are met:
831921Sbrian * 1. Redistributions of source code must retain the above copyright
931921Sbrian *    notice, this list of conditions and the following disclaimer.
1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1131921Sbrian *    notice, this list of conditions and the following disclaimer in the
1231921Sbrian *    documentation and/or other materials provided with the distribution.
1331921Sbrian *
1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1731921Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2431921Sbrian * SUCH DAMAGE.
2531921Sbrian *
2650479Speter * $FreeBSD: releng/11.0/usr.sbin/ppp/server.c 277857 2015-01-28 21:33:49Z dim $
2730715Sbrian */
2830715Sbrian
2971657Sbrian#include <sys/param.h>
3071657Sbrian
3126940Sbrian#include <sys/socket.h>
3226940Sbrian#include <netinet/in.h>
3336285Sbrian#include <sys/un.h>
3430715Sbrian
3530715Sbrian#include <errno.h>
36102500Sbrian#include <stdarg.h>
3730715Sbrian#include <stdio.h>
3826940Sbrian#include <string.h>
3930715Sbrian#include <sys/stat.h>
4036285Sbrian#include <termios.h>
4126940Sbrian#include <unistd.h>
4230715Sbrian
4326940Sbrian#include "log.h"
4436285Sbrian#include "descriptor.h"
4526940Sbrian#include "server.h"
4636285Sbrian#include "prompt.h"
4781634Sbrian#include "ncpaddr.h"
4881900Sbrian#include "probe.h"
4926940Sbrian
5036285Sbrianstatic int
5158028Sbrianserver_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
5236285Sbrian{
5336285Sbrian  struct server *s = descriptor2server(d);
5436314Sbrian  struct prompt *p;
5536314Sbrian  int sets;
5630715Sbrian
5736314Sbrian  sets = 0;
5836285Sbrian  if (r && s->fd >= 0) {
5936285Sbrian    if (*n < s->fd + 1)
6036285Sbrian      *n = s->fd + 1;
6136285Sbrian    FD_SET(s->fd, r);
6236285Sbrian    log_Printf(LogTIMER, "server: fdset(r) %d\n", s->fd);
6336314Sbrian    sets++;
6436285Sbrian  }
6536314Sbrian
6636314Sbrian  for (p = log_PromptList(); p; p = p->next)
6736314Sbrian    sets += descriptor_UpdateSet(&p->desc, r, w, e, n);
6836314Sbrian
6936314Sbrian  return sets;
7036285Sbrian}
7126940Sbrian
7236285Sbrianstatic int
7358028Sbrianserver_IsSet(struct fdescriptor *d, const fd_set *fdset)
7436285Sbrian{
7536285Sbrian  struct server *s = descriptor2server(d);
7636314Sbrian  struct prompt *p;
7736314Sbrian
7836314Sbrian  if (s->fd >= 0 && FD_ISSET(s->fd, fdset))
7936314Sbrian    return 1;
8036314Sbrian
8136314Sbrian  for (p = log_PromptList(); p; p = p->next)
8236314Sbrian    if (descriptor_IsSet(&p->desc, fdset))
8336314Sbrian      return 1;
8436314Sbrian
8536314Sbrian  return 0;
8636285Sbrian}
8736285Sbrian
8836285Sbrianstatic void
8958028Sbrianserver_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
9036285Sbrian{
9136285Sbrian  struct server *s = descriptor2server(d);
9281634Sbrian  struct sockaddr_storage ss;
9381634Sbrian  struct sockaddr *sa = (struct sockaddr *)&ss;
9481634Sbrian  struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
9581634Sbrian#ifndef NOINET6
9681634Sbrian  struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
9781634Sbrian#endif
9881634Sbrian  int ssize = sizeof ss, wfd;
9936285Sbrian  struct prompt *p;
10081634Sbrian  struct ncpaddr addr;
10136285Sbrian
10236314Sbrian  if (s->fd >= 0 && FD_ISSET(s->fd, fdset)) {
10336314Sbrian    wfd = accept(s->fd, sa, &ssize);
10436314Sbrian    if (wfd < 0)
10536314Sbrian      log_Printf(LogERROR, "server_Read: accept(): %s\n", strerror(errno));
10672436Sbrian    else if (sa->sa_len == 0) {
10772436Sbrian      close(wfd);
10872436Sbrian      wfd = -1;
10972436Sbrian    }
11036314Sbrian  } else
11136314Sbrian    wfd = -1;
11236285Sbrian
11336314Sbrian  if (wfd >= 0)
11436285Sbrian    switch (sa->sa_family) {
11536285Sbrian      case AF_LOCAL:
11636314Sbrian        log_Printf(LogPHASE, "Connected to local client.\n");
11736285Sbrian        break;
11836314Sbrian
11936285Sbrian      case AF_INET:
12081634Sbrian        ncpaddr_setsa(&addr, sa);
12181634Sbrian        if (ntohs(sin->sin_port) < 1024) {
12236314Sbrian          log_Printf(LogALERT, "Rejected client connection from %s:%u"
12336314Sbrian                    "(invalid port number) !\n",
12481634Sbrian                    ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
12536314Sbrian          close(wfd);
12636314Sbrian          wfd = -1;
12736314Sbrian          break;
12836314Sbrian        }
12936314Sbrian        log_Printf(LogPHASE, "Connected to client from %s:%u\n",
13081634Sbrian                  ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
13136285Sbrian        break;
13236314Sbrian
13381634Sbrian#ifndef NOINET6
13481634Sbrian      case AF_INET6:
13581634Sbrian        ncpaddr_setsa(&addr, sa);
13681634Sbrian        if (ntohs(sin6->sin6_port) < 1024) {
13781634Sbrian          log_Printf(LogALERT, "Rejected client connection from %s:%u"
13881634Sbrian                    "(invalid port number) !\n",
13981634Sbrian                    ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
14081634Sbrian          close(wfd);
14181634Sbrian          wfd = -1;
14281634Sbrian          break;
14381634Sbrian        }
14481634Sbrian        log_Printf(LogPHASE, "Connected to client from %s:%u\n",
14581634Sbrian                  ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
14681634Sbrian        break;
14781634Sbrian#endif
14881634Sbrian
14936314Sbrian      default:
15036314Sbrian        write(wfd, "Unrecognised access !\n", 22);
15136314Sbrian        close(wfd);
15236314Sbrian        wfd = -1;
15336314Sbrian        break;
15436285Sbrian    }
15536314Sbrian
15636314Sbrian  if (wfd >= 0) {
15736314Sbrian    if ((p = prompt_Create(s, bundle, wfd)) == NULL) {
15836314Sbrian      write(wfd, "Connection refused.\n", 20);
15936314Sbrian      close(wfd);
16036314Sbrian    } else {
16136314Sbrian      switch (sa->sa_family) {
16236314Sbrian        case AF_LOCAL:
16336314Sbrian          p->src.type = "local";
16471657Sbrian          strncpy(p->src.from, s->cfg.sockname, sizeof p->src.from - 1);
16536314Sbrian          p->src.from[sizeof p->src.from - 1] = '\0';
16636314Sbrian          break;
16736314Sbrian        case AF_INET:
16881634Sbrian          p->src.type = "ip";
16936314Sbrian          snprintf(p->src.from, sizeof p->src.from, "%s:%u",
17081634Sbrian                   ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
17136314Sbrian          break;
17281634Sbrian#ifndef NOINET6
17381634Sbrian        case AF_INET6:
17481634Sbrian          p->src.type = "ip6";
17581634Sbrian          snprintf(p->src.from, sizeof p->src.from, "%s:%u",
17681634Sbrian                   ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
17781634Sbrian          break;
17881634Sbrian#endif
17936314Sbrian      }
18036314Sbrian      prompt_TtyCommandMode(p);
18136314Sbrian      prompt_Required(p);
18236314Sbrian    }
18336285Sbrian  }
18436314Sbrian
18538013Sbrian  log_PromptListChanged = 0;
18636314Sbrian  for (p = log_PromptList(); p; p = p->next)
18738013Sbrian    if (descriptor_IsSet(&p->desc, fdset)) {
18836314Sbrian      descriptor_Read(&p->desc, bundle, fdset);
18938013Sbrian      if (log_PromptListChanged)
19038013Sbrian        break;
19138013Sbrian    }
19236285Sbrian}
19336285Sbrian
19437141Sbrianstatic int
195134789Sbrianserver_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
196134789Sbrian	     const fd_set *fdset __unused)
19736285Sbrian{
19836285Sbrian  /* We never want to write here ! */
19937019Sbrian  log_Printf(LogALERT, "server_Write: Internal error: Bad call !\n");
20037141Sbrian  return 0;
20136285Sbrian}
20236285Sbrian
20336285Sbrianstruct server server = {
20436285Sbrian  {
20536285Sbrian    SERVER_DESCRIPTOR,
20636285Sbrian    server_UpdateSet,
20736285Sbrian    server_IsSet,
20836285Sbrian    server_Read,
20936285Sbrian    server_Write
21036285Sbrian  },
211134789Sbrian  -1,
212134789Sbrian  { "", "", 0, 0 }
21336285Sbrian};
21436285Sbrian
21571657Sbrianenum server_stat
21671657Sbrianserver_Reopen(struct bundle *bundle)
21771657Sbrian{
21871657Sbrian  char name[sizeof server.cfg.sockname];
21971764Sbrian  struct stat st;
22071657Sbrian  u_short port;
22171657Sbrian  mode_t mask;
22271657Sbrian  enum server_stat ret;
22371657Sbrian
22471657Sbrian  if (server.cfg.sockname[0] != '\0') {
22571657Sbrian    strcpy(name, server.cfg.sockname);
22671657Sbrian    mask = server.cfg.mask;
22771657Sbrian    server_Close(bundle);
22871764Sbrian    if (server.cfg.sockname[0] != '\0' && stat(server.cfg.sockname, &st) == 0)
22971764Sbrian      if (!(st.st_mode & S_IFSOCK) || unlink(server.cfg.sockname) != 0)
23071764Sbrian        return SERVER_FAILED;
23171657Sbrian    ret = server_LocalOpen(bundle, name, mask);
23271657Sbrian  } else if (server.cfg.port != 0) {
23371657Sbrian    port = server.cfg.port;
23471657Sbrian    server_Close(bundle);
23571657Sbrian    ret = server_TcpOpen(bundle, port);
23671657Sbrian  } else
23771657Sbrian    ret = SERVER_UNSET;
23871657Sbrian
23971657Sbrian  return ret;
24071657Sbrian}
24171657Sbrian
24271657Sbrianenum server_stat
24336285Sbrianserver_LocalOpen(struct bundle *bundle, const char *name, mode_t mask)
24426940Sbrian{
24571657Sbrian  struct sockaddr_un ifsun;
24671657Sbrian  mode_t oldmask;
24728679Sbrian  int s;
24826940Sbrian
24971657Sbrian  oldmask = (mode_t)-1;		/* Silence compiler */
25029083Sbrian
251277857Sdim  if (server.cfg.sockname[0] != '\0' && !strcmp(server.cfg.sockname, name))
25271657Sbrian    server_Close(bundle);
25371657Sbrian
25471657Sbrian  memset(&ifsun, '\0', sizeof ifsun);
25571657Sbrian  ifsun.sun_len = strlen(name);
25671657Sbrian  if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
25736285Sbrian    log_Printf(LogERROR, "Local: %s: Path too long\n", name);
25871657Sbrian    return SERVER_INVALID;
25928679Sbrian  }
26071657Sbrian  ifsun.sun_family = AF_LOCAL;
26171657Sbrian  strcpy(ifsun.sun_path, name);
26226940Sbrian
26389422Sbrian  s = socket(PF_LOCAL, SOCK_STREAM, 0);
26428679Sbrian  if (s < 0) {
26536285Sbrian    log_Printf(LogERROR, "Local: socket: %s\n", strerror(errno));
26671657Sbrian    goto failed;
26728679Sbrian  }
26828679Sbrian  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
26931081Sbrian  if (mask != (mode_t)-1)
27071657Sbrian    oldmask = umask(mask);
27171657Sbrian  if (bind(s, (struct sockaddr *)&ifsun, sizeof ifsun) < 0) {
27231081Sbrian    if (mask != (mode_t)-1)
27371657Sbrian      umask(oldmask);
27436285Sbrian    log_Printf(LogWARN, "Local: bind: %s\n", strerror(errno));
27528679Sbrian    close(s);
27671657Sbrian    goto failed;
27728679Sbrian  }
27831081Sbrian  if (mask != (mode_t)-1)
27971657Sbrian    umask(oldmask);
28028679Sbrian  if (listen(s, 5) != 0) {
28167912Sbrian    log_Printf(LogERROR, "Local: Unable to listen to socket -"
28267912Sbrian               " BUNDLE overload?\n");
28328679Sbrian    close(s);
28471657Sbrian    unlink(name);
28571657Sbrian    goto failed;
28628679Sbrian  }
28744588Sbrian  server_Close(bundle);
28836285Sbrian  server.fd = s;
28971657Sbrian  server.cfg.port = 0;
29071657Sbrian  strncpy(server.cfg.sockname, ifsun.sun_path, sizeof server.cfg.sockname - 1);
29171657Sbrian  server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
29271657Sbrian  server.cfg.mask = mask;
29336285Sbrian  log_Printf(LogPHASE, "Listening at local socket %s.\n", name);
29471657Sbrian
29571657Sbrian  return SERVER_OK;
29671657Sbrian
29771657Sbrianfailed:
29871657Sbrian  if (server.fd == -1) {
29971657Sbrian    server.fd = -1;
30071657Sbrian    server.cfg.port = 0;
30171657Sbrian    strncpy(server.cfg.sockname, ifsun.sun_path,
30271657Sbrian            sizeof server.cfg.sockname - 1);
30371657Sbrian    server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
30471657Sbrian    server.cfg.mask = mask;
30571657Sbrian  }
30671657Sbrian  return SERVER_FAILED;
30726940Sbrian}
30826940Sbrian
30971657Sbrianenum server_stat
31071657Sbrianserver_TcpOpen(struct bundle *bundle, u_short port)
31126940Sbrian{
31281634Sbrian  struct sockaddr_storage ss;
31381634Sbrian  struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
31481900Sbrian#ifndef NOINET6
31581634Sbrian  struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
31681634Sbrian#endif
31781634Sbrian  int s, sz;
31826940Sbrian
31971657Sbrian  if (server.cfg.port == port)
32071657Sbrian    server_Close(bundle);
32129252Sbrian
32271657Sbrian  if (port == 0)
32371657Sbrian    return SERVER_INVALID;
32471657Sbrian
32581634Sbrian  memset(&ss, '\0', sizeof ss);
32681900Sbrian#ifndef NOINET6
32781900Sbrian  if (probe.ipv6_available) {
32881900Sbrian    sin6->sin6_family = AF_INET6;
32981900Sbrian    sin6->sin6_port = htons(port);
33081924Sbrian    sin6->sin6_len = (u_int8_t)sizeof ss;
33181900Sbrian    sz = sizeof *sin6;
33289422Sbrian    s = socket(PF_INET6, SOCK_STREAM, 0);
33381900Sbrian  } else
33481634Sbrian#endif
33581900Sbrian  {
33681900Sbrian    sin->sin_family = AF_INET;
33781900Sbrian    sin->sin_port = htons(port);
33881924Sbrian    sin->sin_len = (u_int8_t)sizeof ss;
33981900Sbrian    sin->sin_addr.s_addr = INADDR_ANY;
34081900Sbrian    sz = sizeof *sin;
34189422Sbrian    s = socket(PF_INET, SOCK_STREAM, 0);
34281900Sbrian  }
34381900Sbrian
34428679Sbrian  if (s < 0) {
34536285Sbrian    log_Printf(LogERROR, "Tcp: socket: %s\n", strerror(errno));
34671657Sbrian    goto failed;
34728679Sbrian  }
34881634Sbrian
349162389Sume#ifndef NOINET6
350162389Sume  if (probe.ipv6_available) {
351162389Sume    int off = 0;
352162389Sume    setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off));
353162389Sume  }
354162389Sume#endif
355162389Sume
35628679Sbrian  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
35781634Sbrian  if (bind(s, (struct sockaddr *)&ss, sz) < 0) {
35836285Sbrian    log_Printf(LogWARN, "Tcp: bind: %s\n", strerror(errno));
35928679Sbrian    close(s);
36071657Sbrian    goto failed;
36128679Sbrian  }
36228679Sbrian  if (listen(s, 5) != 0) {
36344588Sbrian    log_Printf(LogERROR, "Tcp: Unable to listen to socket: %s\n",
36444588Sbrian               strerror(errno));
36528679Sbrian    close(s);
36671657Sbrian    goto failed;
36728679Sbrian  }
36836285Sbrian  server_Close(bundle);
36936285Sbrian  server.fd = s;
37071657Sbrian  server.cfg.port = port;
37171657Sbrian  *server.cfg.sockname = '\0';
37271657Sbrian  server.cfg.mask = 0;
37336285Sbrian  log_Printf(LogPHASE, "Listening at port %d.\n", port);
37471657Sbrian  return SERVER_OK;
37571657Sbrian
37671657Sbrianfailed:
37771657Sbrian  if (server.fd == -1) {
37871657Sbrian    server.fd = -1;
37971657Sbrian    server.cfg.port = port;
38071657Sbrian    *server.cfg.sockname = '\0';
38171657Sbrian    server.cfg.mask = 0;
38271657Sbrian  }
38371657Sbrian  return SERVER_FAILED;
38426940Sbrian}
38526940Sbrian
38636285Sbrianint
387134789Sbrianserver_Close(struct bundle *bundle __unused)
38826940Sbrian{
38936285Sbrian  if (server.fd >= 0) {
39071657Sbrian    if (*server.cfg.sockname != '\0') {
39144588Sbrian      struct sockaddr_un un;
39244588Sbrian      int sz = sizeof un;
39344588Sbrian
39444588Sbrian      if (getsockname(server.fd, (struct sockaddr *)&un, &sz) == 0 &&
39544588Sbrian          un.sun_family == AF_LOCAL && sz == sizeof un)
39671657Sbrian        unlink(un.sun_path);
39726940Sbrian    }
39844588Sbrian    close(server.fd);
39936285Sbrian    server.fd = -1;
40036285Sbrian    /* Drop associated prompts */
40136314Sbrian    log_DestroyPrompts(&server);
40271657Sbrian
40336285Sbrian    return 1;
40426940Sbrian  }
40571657Sbrian
40636285Sbrian  return 0;
40726940Sbrian}
40871657Sbrian
40971657Sbrianint
41071657Sbrianserver_Clear(struct bundle *bundle)
41171657Sbrian{
41271657Sbrian  int ret;
41371657Sbrian
41471657Sbrian  ret = server_Close(bundle);
41571657Sbrian
41671657Sbrian  server.fd = -1;
41771657Sbrian  server.cfg.port = 0;
41871657Sbrian  *server.cfg.sockname = '\0';
41971657Sbrian  server.cfg.mask = 0;
42071657Sbrian
42171657Sbrian  return ret;
42271657Sbrian}
423