| /*- |
| * Copyright (c) 1997 Brian Somers <[email protected]> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * $FreeBSD: src/usr.sbin/ppp/server.c,v 1.44.14.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #include <sys/param.h> |
| |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <sys/un.h> |
| |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <termios.h> |
| #include <unistd.h> |
| |
| #include "log.h" |
| #include "descriptor.h" |
| #include "server.h" |
| #include "prompt.h" |
| #include "ncpaddr.h" |
| #include "probe.h" |
| |
| static int |
| server_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n) |
| { |
| struct server *s = descriptor2server(d); |
| struct prompt *p; |
| int sets; |
| |
| sets = 0; |
| if (r && s->fd >= 0) { |
| if (*n < s->fd + 1) |
| *n = s->fd + 1; |
| FD_SET(s->fd, r); |
| log_Printf(LogTIMER, "server: fdset(r) %d\n", s->fd); |
| sets++; |
| } |
| |
| for (p = log_PromptList(); p; p = p->next) |
| sets += descriptor_UpdateSet(&p->desc, r, w, e, n); |
| |
| return sets; |
| } |
| |
| static int |
| server_IsSet(struct fdescriptor *d, const fd_set *fdset) |
| { |
| struct server *s = descriptor2server(d); |
| struct prompt *p; |
| |
| if (s->fd >= 0 && FD_ISSET(s->fd, fdset)) |
| return 1; |
| |
| for (p = log_PromptList(); p; p = p->next) |
| if (descriptor_IsSet(&p->desc, fdset)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static void |
| server_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset) |
| { |
| struct server *s = descriptor2server(d); |
| struct sockaddr_storage ss; |
| struct sockaddr *sa = (struct sockaddr *)&ss; |
| struct sockaddr_in *sin = (struct sockaddr_in *)&ss; |
| #ifndef NOINET6 |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; |
| #endif |
| int ssize = sizeof ss, wfd; |
| struct prompt *p; |
| struct ncpaddr addr; |
| |
| if (s->fd >= 0 && FD_ISSET(s->fd, fdset)) { |
| wfd = accept(s->fd, sa, &ssize); |
| if (wfd < 0) |
| log_Printf(LogERROR, "server_Read: accept(): %s\n", strerror(errno)); |
| else if (sa->sa_len == 0) { |
| close(wfd); |
| wfd = -1; |
| } |
| } else |
| wfd = -1; |
| |
| if (wfd >= 0) |
| switch (sa->sa_family) { |
| case AF_LOCAL: |
| log_Printf(LogPHASE, "Connected to local client.\n"); |
| break; |
| |
| case AF_INET: |
| ncpaddr_setsa(&addr, sa); |
| if (ntohs(sin->sin_port) < 1024) { |
| log_Printf(LogALERT, "Rejected client connection from %s:%u" |
| "(invalid port number) !\n", |
| ncpaddr_ntoa(&addr), ntohs(sin->sin_port)); |
| close(wfd); |
| wfd = -1; |
| break; |
| } |
| log_Printf(LogPHASE, "Connected to client from %s:%u\n", |
| ncpaddr_ntoa(&addr), ntohs(sin->sin_port)); |
| break; |
| |
| #ifndef NOINET6 |
| case AF_INET6: |
| ncpaddr_setsa(&addr, sa); |
| if (ntohs(sin6->sin6_port) < 1024) { |
| log_Printf(LogALERT, "Rejected client connection from %s:%u" |
| "(invalid port number) !\n", |
| ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port)); |
| close(wfd); |
| wfd = -1; |
| break; |
| } |
| log_Printf(LogPHASE, "Connected to client from %s:%u\n", |
| ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port)); |
| break; |
| #endif |
| |
| default: |
| write(wfd, "Unrecognised access !\n", 22); |
| close(wfd); |
| wfd = -1; |
| break; |
| } |
| |
| if (wfd >= 0) { |
| if ((p = prompt_Create(s, bundle, wfd)) == NULL) { |
| write(wfd, "Connection refused.\n", 20); |
| close(wfd); |
| } else { |
| switch (sa->sa_family) { |
| case AF_LOCAL: |
| p->src.type = "local"; |
| strncpy(p->src.from, s->cfg.sockname, sizeof p->src.from - 1); |
| p->src.from[sizeof p->src.from - 1] = '\0'; |
| break; |
| case AF_INET: |
| p->src.type = "ip"; |
| snprintf(p->src.from, sizeof p->src.from, "%s:%u", |
| ncpaddr_ntoa(&addr), ntohs(sin->sin_port)); |
| break; |
| #ifndef NOINET6 |
| case AF_INET6: |
| p->src.type = "ip6"; |
| snprintf(p->src.from, sizeof p->src.from, "%s:%u", |
| ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port)); |
| break; |
| #endif |
| } |
| prompt_TtyCommandMode(p); |
| prompt_Required(p); |
| } |
| } |
| |
| log_PromptListChanged = 0; |
| for (p = log_PromptList(); p; p = p->next) |
| if (descriptor_IsSet(&p->desc, fdset)) { |
| descriptor_Read(&p->desc, bundle, fdset); |
| if (log_PromptListChanged) |
| break; |
| } |
| } |
| |
| static int |
| server_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused, |
| const fd_set *fdset __unused) |
| { |
| /* We never want to write here ! */ |
| log_Printf(LogALERT, "server_Write: Internal error: Bad call !\n"); |
| return 0; |
| } |
| |
| struct server server = { |
| { |
| SERVER_DESCRIPTOR, |
| server_UpdateSet, |
| server_IsSet, |
| server_Read, |
| server_Write |
| }, |
| -1, |
| { "", "", 0, 0 } |
| }; |
| |
| enum server_stat |
| server_Reopen(struct bundle *bundle) |
| { |
| char name[sizeof server.cfg.sockname]; |
| struct stat st; |
| u_short port; |
| mode_t mask; |
| enum server_stat ret; |
| |
| if (server.cfg.sockname[0] != '\0') { |
| strcpy(name, server.cfg.sockname); |
| mask = server.cfg.mask; |
| server_Close(bundle); |
| if (server.cfg.sockname[0] != '\0' && stat(server.cfg.sockname, &st) == 0) |
| if (!(st.st_mode & S_IFSOCK) || unlink(server.cfg.sockname) != 0) |
| return SERVER_FAILED; |
| ret = server_LocalOpen(bundle, name, mask); |
| } else if (server.cfg.port != 0) { |
| port = server.cfg.port; |
| server_Close(bundle); |
| ret = server_TcpOpen(bundle, port); |
| } else |
| ret = SERVER_UNSET; |
| |
| return ret; |
| } |
| |
| enum server_stat |
| server_LocalOpen(struct bundle *bundle, const char *name, mode_t mask) |
| { |
| struct sockaddr_un ifsun; |
| mode_t oldmask; |
| int s; |
| |
| oldmask = (mode_t)-1; /* Silence compiler */ |
| |
| if (server.cfg.sockname && !strcmp(server.cfg.sockname, name)) |
| server_Close(bundle); |
| |
| memset(&ifsun, '\0', sizeof ifsun); |
| ifsun.sun_len = strlen(name); |
| if (ifsun.sun_len > sizeof ifsun.sun_path - 1) { |
| log_Printf(LogERROR, "Local: %s: Path too long\n", name); |
| return SERVER_INVALID; |
| } |
| ifsun.sun_family = AF_LOCAL; |
| strcpy(ifsun.sun_path, name); |
| |
| s = socket(PF_LOCAL, SOCK_STREAM, 0); |
| if (s < 0) { |
| log_Printf(LogERROR, "Local: socket: %s\n", strerror(errno)); |
| goto failed; |
| } |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s); |
| if (mask != (mode_t)-1) |
| oldmask = umask(mask); |
| if (bind(s, (struct sockaddr *)&ifsun, sizeof ifsun) < 0) { |
| if (mask != (mode_t)-1) |
| umask(oldmask); |
| log_Printf(LogWARN, "Local: bind: %s\n", strerror(errno)); |
| close(s); |
| goto failed; |
| } |
| if (mask != (mode_t)-1) |
| umask(oldmask); |
| if (listen(s, 5) != 0) { |
| log_Printf(LogERROR, "Local: Unable to listen to socket -" |
| " BUNDLE overload?\n"); |
| close(s); |
| unlink(name); |
| goto failed; |
| } |
| server_Close(bundle); |
| server.fd = s; |
| server.cfg.port = 0; |
| strncpy(server.cfg.sockname, ifsun.sun_path, sizeof server.cfg.sockname - 1); |
| server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0'; |
| server.cfg.mask = mask; |
| log_Printf(LogPHASE, "Listening at local socket %s.\n", name); |
| |
| return SERVER_OK; |
| |
| failed: |
| if (server.fd == -1) { |
| server.fd = -1; |
| server.cfg.port = 0; |
| strncpy(server.cfg.sockname, ifsun.sun_path, |
| sizeof server.cfg.sockname - 1); |
| server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0'; |
| server.cfg.mask = mask; |
| } |
| return SERVER_FAILED; |
| } |
| |
| enum server_stat |
| server_TcpOpen(struct bundle *bundle, u_short port) |
| { |
| struct sockaddr_storage ss; |
| struct sockaddr_in *sin = (struct sockaddr_in *)&ss; |
| #ifndef NOINET6 |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss; |
| #endif |
| int s, sz; |
| |
| if (server.cfg.port == port) |
| server_Close(bundle); |
| |
| if (port == 0) |
| return SERVER_INVALID; |
| |
| memset(&ss, '\0', sizeof ss); |
| #ifndef NOINET6 |
| if (probe.ipv6_available) { |
| sin6->sin6_family = AF_INET6; |
| sin6->sin6_port = htons(port); |
| sin6->sin6_len = (u_int8_t)sizeof ss; |
| sz = sizeof *sin6; |
| s = socket(PF_INET6, SOCK_STREAM, 0); |
| } else |
| #endif |
| { |
| sin->sin_family = AF_INET; |
| sin->sin_port = htons(port); |
| sin->sin_len = (u_int8_t)sizeof ss; |
| sin->sin_addr.s_addr = INADDR_ANY; |
| sz = sizeof *sin; |
| s = socket(PF_INET, SOCK_STREAM, 0); |
| } |
| |
| if (s < 0) { |
| log_Printf(LogERROR, "Tcp: socket: %s\n", strerror(errno)); |
| goto failed; |
| } |
| |
| #ifndef NOINET6 |
| if (probe.ipv6_available) { |
| int off = 0; |
| setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off)); |
| } |
| #endif |
| |
| setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s); |
| if (bind(s, (struct sockaddr *)&ss, sz) < 0) { |
| log_Printf(LogWARN, "Tcp: bind: %s\n", strerror(errno)); |
| close(s); |
| goto failed; |
| } |
| if (listen(s, 5) != 0) { |
| log_Printf(LogERROR, "Tcp: Unable to listen to socket: %s\n", |
| strerror(errno)); |
| close(s); |
| goto failed; |
| } |
| server_Close(bundle); |
| server.fd = s; |
| server.cfg.port = port; |
| *server.cfg.sockname = '\0'; |
| server.cfg.mask = 0; |
| log_Printf(LogPHASE, "Listening at port %d.\n", port); |
| return SERVER_OK; |
| |
| failed: |
| if (server.fd == -1) { |
| server.fd = -1; |
| server.cfg.port = port; |
| *server.cfg.sockname = '\0'; |
| server.cfg.mask = 0; |
| } |
| return SERVER_FAILED; |
| } |
| |
| int |
| server_Close(struct bundle *bundle __unused) |
| { |
| if (server.fd >= 0) { |
| if (*server.cfg.sockname != '\0') { |
| struct sockaddr_un un; |
| int sz = sizeof un; |
| |
| if (getsockname(server.fd, (struct sockaddr *)&un, &sz) == 0 && |
| un.sun_family == AF_LOCAL && sz == sizeof un) |
| unlink(un.sun_path); |
| } |
| close(server.fd); |
| server.fd = -1; |
| /* Drop associated prompts */ |
| log_DestroyPrompts(&server); |
| |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| server_Clear(struct bundle *bundle) |
| { |
| int ret; |
| |
| ret = server_Close(bundle); |
| |
| server.fd = -1; |
| server.cfg.port = 0; |
| *server.cfg.sockname = '\0'; |
| server.cfg.mask = 0; |
| |
| return ret; |
| } |