diff options
| -rw-r--r-- | accel-pppd/accel-ppp.conf.5 | 14 | ||||
| -rw-r--r-- | accel-pppd/cli/cli_p.h | 84 | ||||
| -rw-r--r-- | accel-pppd/cli/tcp.c | 52 | ||||
| -rw-r--r-- | accel-pppd/cli/telnet.c | 52 |
4 files changed, 144 insertions, 58 deletions
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5 index cefc74ed..3e645b85 100644 --- a/accel-pppd/accel-ppp.conf.5 +++ b/accel-pppd/accel-ppp.conf.5 @@ -1372,13 +1372,19 @@ If \fIn\fR = 2 then cli module will also log passed commands. .TP .BI "tcp=" host:port Defines on which IP address and port the TCP module will listen for incoming -connections. When \fIhost\fR is empty, the TCP module listens on all local -interfaces. It isn't loaded if this option isn't defined. +connections. \fIhost\fR may be an IPv4 or an IPv6 address; IPv6 addresses may +be enclosed in square brackets (e.g. \fI[::1]:2001\fR). When \fIhost\fR is +empty, the TCP module listens on all local interfaces (use \fI[::]:port\fR to +listen on all interfaces over IPv6). It isn't loaded if this option isn't +defined. .TP .BI "telnet=" host:port Defines on which IP address and port the Telnet module will listen for incoming -connections. When \fIhost\fR is empty, the Telnet module listens on all local -interfaces. It isn't loaded if this option isn't defined. +connections. \fIhost\fR may be an IPv4 or an IPv6 address; IPv6 addresses may +be enclosed in square brackets (e.g. \fI[::1]:2000\fR). When \fIhost\fR is +empty, the Telnet module listens on all local interfaces (use \fI[::]:port\fR +to listen on all interfaces over IPv6). It isn't loaded if this option isn't +defined. .TP .BI "password=" passwd Defines the password to be used by the TCP and Telnet modules for diff --git a/accel-pppd/cli/cli_p.h b/accel-pppd/cli/cli_p.h index 0fcba309..79208e13 100644 --- a/accel-pppd/cli/cli_p.h +++ b/accel-pppd/cli/cli_p.h @@ -2,6 +2,11 @@ #define __CLI_P_H #include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> #include "triton.h" @@ -15,6 +20,85 @@ struct cli_client_t int cli_process_cmd(struct cli_client_t *cln); +/* Format peer address (IPv4, IPv6 or IPv4-mapped IPv6) for logging. + * buf must be at least INET6_ADDRSTRLEN bytes long. */ +static inline const char *cli_addr_str(const struct sockaddr_storage *addr, + char *buf, size_t size) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)addr; + const struct sockaddr_in *sin = (const struct sockaddr_in *)addr; + + buf[0] = '\0'; + if (addr->ss_family == AF_INET6) { + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + inet_ntop(AF_INET, &sin6->sin6_addr.s6_addr32[3], buf, size); + else + inet_ntop(AF_INET6, &sin6->sin6_addr, buf, size); + } else + inet_ntop(AF_INET, &sin->sin_addr, buf, size); + + return buf; +} + +/* Parse "host:port", "[host]:port" or ":port" listener specification. + * str is modified in place, *host points into str afterwards (NULL for + * empty host). For unbracketed hosts the last ':' separates the port, + * so bare IPv6 addresses like "::1:2001" are accepted too. + * Returns 0 on success, -1 on invalid format. */ +static inline int cli_parse_hostport(char *str, const char **host, int *port) +{ + char *d; + + if (*str == '[') { + ++str; + d = strchr(str, ']'); + if (!d || d[1] != ':') + return -1; + *d++ = '\0'; + } else { + d = strrchr(str, ':'); + if (!d) + return -1; + } + + *d = '\0'; + *port = atoi(d + 1); + if (*port <= 0) + return -1; + + *host = *str ? str : NULL; + + return 0; +} + +/* Fill sockaddr for binding a CLI listener. host may be an IPv4 or IPv6 + * address; NULL host means any IPv4 address (use "::" for IPv6 wildcard). + * Returns 0 on success, -1 if host is not a valid address. */ +static inline int cli_bind_addr(const char *host, int port, + struct sockaddr_storage *addr, socklen_t *len) +{ + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + + memset(addr, 0, sizeof(*addr)); + + if (host && inet_pton(AF_INET6, host, &sin6->sin6_addr) > 0) { + sin6->sin6_family = AF_INET6; + sin6->sin6_port = htons(port); + *len = sizeof(*sin6); + } else { + sin->sin_family = AF_INET; + sin->sin_port = htons(port); + if (!host) + sin->sin_addr.s_addr = htonl(INADDR_ANY); + else if (inet_pton(AF_INET, host, &sin->sin_addr) <= 0) + return -1; + *len = sizeof(*sin); + } + + return 0; +} + extern char *conf_cli_passwd; extern char *conf_cli_prompt; diff --git a/accel-pppd/cli/tcp.c b/accel-pppd/cli/tcp.c index 8952661a..beba16ca 100644 --- a/accel-pppd/cli/tcp.c +++ b/accel-pppd/cli/tcp.c @@ -24,7 +24,7 @@ struct tcp_client_t { struct cli_client_t cli_client; struct list_head entry; struct triton_md_handler_t hnd; - struct sockaddr_in addr; + struct sockaddr_storage addr; struct list_head xmit_queue; struct buffer_t *xmit_buf; uint8_t *cmdline; @@ -174,8 +174,10 @@ static int cln_read(struct triton_md_handler_t *h) goto disconn_hard; cln->auth = 1; } else { - if (conf_verbose == 2) - log_info2("cli: %s: %s\n", inet_ntoa(cln->addr.sin_addr), cln->cmdline); + if (conf_verbose == 2) { + char buf[INET6_ADDRSTRLEN]; + log_info2("cli: %s: %s\n", cli_addr_str(&cln->addr, buf, sizeof(buf)), cln->cmdline); + } cli_process_cmd(&cln->cli_client); } @@ -248,12 +250,14 @@ disconn: static int serv_read(struct triton_md_handler_t *h) { - struct sockaddr_in addr; - socklen_t size = sizeof(addr); + struct sockaddr_storage addr; + socklen_t size; int sock; struct tcp_client_t *conn; + char buf[INET6_ADDRSTRLEN]; while(1) { + size = sizeof(addr); sock = accept(h->fd, (struct sockaddr *)&addr, &size); if (sock < 0) { if (errno == EAGAIN) @@ -263,7 +267,7 @@ static int serv_read(struct triton_md_handler_t *h) } if (conf_verbose) - log_info2("cli: tcp: new connection from %s\n", inet_ntoa(addr.sin_addr)); + log_info2("cli: tcp: new connection from %s\n", cli_addr_str(&addr, buf, sizeof(buf))); if (fcntl(sock, F_SETFL, O_NONBLOCK)) { log_error("cli: tcp: failed to set nonblocking mode: %s, closing connection...\n", strerror(errno)); @@ -320,9 +324,16 @@ static struct triton_md_handler_t serv_hnd = { static void start_server(const char *host, int port) { - struct sockaddr_in addr; + struct sockaddr_storage addr; + socklen_t addrlen; + int f = 1; + + if (cli_bind_addr(host, port, &addr, &addrlen) < 0) { + log_emerg("cli: tcp: invalid address '%s'\n", host); + return; + } - serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0); + serv_hnd.fd = socket(addr.ss_family, SOCK_STREAM, 0); if (serv_hnd.fd < 0) { log_emerg("cli: tcp: failed to create server socket: %s\n", strerror(errno)); return; @@ -330,16 +341,8 @@ static void start_server(const char *host, int port) fcntl(serv_hnd.fd, F_SETFD, fcntl(serv_hnd.fd, F_GETFD) | FD_CLOEXEC); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (host) - addr.sin_addr.s_addr = inet_addr(host); - else - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4); - if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) { + setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f)); + if (bind (serv_hnd.fd, (struct sockaddr *) &addr, addrlen) < 0) { log_emerg("cli: tcp: failed to bind socket: %s\n", strerror(errno)); close(serv_hnd.fd); return; @@ -378,7 +381,8 @@ static void load_config(void) static void init(void) { const char *opt; - char *host, *d; + const char *addr; + char *host; int port; opt = conf_get_opt("cli", "tcp"); @@ -386,20 +390,14 @@ static void init(void) return; host = strdup(opt); - d = strstr(host, ":"); - if (!d) - goto err_fmt; - - *d = 0; - port = atoi(d + 1); - if (port <= 0) + if (cli_parse_hostport(host, &addr, &port) < 0) goto err_fmt; load_config(); temp_buf = malloc(RECV_BUF_SIZE); - start_server(host, port); + start_server(addr, port); triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); diff --git a/accel-pppd/cli/telnet.c b/accel-pppd/cli/telnet.c index 7d5009c2..88a9d8b0 100644 --- a/accel-pppd/cli/telnet.c +++ b/accel-pppd/cli/telnet.c @@ -37,7 +37,7 @@ struct telnet_client_t { struct cli_client_t cli_client; struct list_head entry; struct triton_md_handler_t hnd; - struct sockaddr_in addr; + struct sockaddr_storage addr; struct list_head xmit_queue; struct buffer_t *xmit_buf; int xmit_pos; @@ -305,8 +305,10 @@ static int telnet_input_char(struct telnet_client_t *cln, uint8_t c) list_add(&b->entry, cln->history.next); cln->history_pos = cln->history.next; - if (conf_verbose == 2) - log_info2("cli: %s: %s\n", inet_ntoa(cln->addr.sin_addr), cln->cmdline); + if (conf_verbose == 2) { + char abuf[INET6_ADDRSTRLEN]; + log_info2("cli: %s: %s\n", cli_addr_str(&cln->addr, abuf, sizeof(abuf)), cln->cmdline); + } if (cli_process_cmd(&cln->cli_client)) return -1; @@ -556,13 +558,15 @@ disconn: static int serv_read(struct triton_md_handler_t *h) { - struct sockaddr_in addr; - socklen_t size = sizeof(addr); + struct sockaddr_storage addr; + socklen_t size; int sock; struct telnet_client_t *conn; struct buffer_t *b, *b2; + char abuf[INET6_ADDRSTRLEN]; while(1) { + size = sizeof(addr); sock = accept(h->fd, (struct sockaddr *)&addr, &size); if (sock < 0) { if (errno == EAGAIN) @@ -572,7 +576,7 @@ static int serv_read(struct triton_md_handler_t *h) } if (conf_verbose) - log_info2("cli: telnet: new connection from %s\n", inet_ntoa(addr.sin_addr)); + log_info2("cli: telnet: new connection from %s\n", cli_addr_str(&addr, abuf, sizeof(abuf))); fcntl(sock, F_SETFL, O_NONBLOCK); fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); @@ -657,9 +661,16 @@ static struct triton_md_handler_t serv_hnd = { static void start_server(const char *host, int port) { - struct sockaddr_in addr; + struct sockaddr_storage addr; + socklen_t addrlen; + int f = 1; + + if (cli_bind_addr(host, port, &addr, &addrlen) < 0) { + log_emerg("cli: telnet: invalid address '%s'\n", host); + return; + } - serv_hnd.fd = socket(PF_INET, SOCK_STREAM, 0); + serv_hnd.fd = socket(addr.ss_family, SOCK_STREAM, 0); if (serv_hnd.fd < 0) { log_emerg("cli: telnet: failed to create server socket: %s\n", strerror(errno)); return; @@ -667,16 +678,8 @@ static void start_server(const char *host, int port) fcntl(serv_hnd.fd, F_SETFD, fcntl(serv_hnd.fd, F_GETFD) | FD_CLOEXEC); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (host) - addr.sin_addr.s_addr = inet_addr(host); - else - addr.sin_addr.s_addr = htonl(INADDR_ANY); - - setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &serv_hnd.fd, 4); - if (bind (serv_hnd.fd, (struct sockaddr *) &addr, sizeof (addr)) < 0) { + setsockopt(serv_hnd.fd, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f)); + if (bind (serv_hnd.fd, (struct sockaddr *) &addr, addrlen) < 0) { log_emerg("cli: telnet: failed to bind socket: %s\n", strerror(errno)); close(serv_hnd.fd); return; @@ -755,7 +758,8 @@ static void load_config(void) static void init(void) { const char *opt; - char *host, *d; + const char *addr; + char *host; int port; opt = conf_get_opt("cli", "telnet"); @@ -763,13 +767,7 @@ static void init(void) return; host = strdup(opt); - d = strstr(host, ":"); - if (!d) - goto err_fmt; - - *d = 0; - port = atoi(d + 1); - if (port <= 0) + if (cli_parse_hostport(host, &addr, &port) < 0) goto err_fmt; opt = conf_get_opt("cli", "history-file"); @@ -783,7 +781,7 @@ static void init(void) load_history_file(); - start_server(host, port); + start_server(addr, port); atexit(save_history_file); |
