/* $OpenBSD: npppdctl.c,v 1.6 2010/09/22 00:32:48 jsg Exp $ */
/*-
* Copyright (c) 2009 Internet Initiative Japan Inc.
* 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.
*/
/* $Id: npppdctl.c,v 1.6 2010/09/22 00:32:48 jsg Exp $ */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "debugutil.h"
#include "npppd_local.h"
#include "npppd_ctl.h"
#ifndef MIN
#define MIN(m,n) ((m) < (n))? (m) : (n)
#endif
#define DEFAULT_TIMEOUT 5
/** Filename template for listening soccket of UNIX domain datagram */
char dgramsock[] = "/tmp/npppdctl.XXXXXX";
/** Daemon side socket address */
struct sockaddr_un peersock;
/** Socket descriptor */
int sock = -1;
/** Show 'since' field as unix time. */
int uflag = 0;
/** Don't convert addresses/ports to names */
int nflag = 0;
/** Receive buffer size */
int rcvbuf_sz = DEFAULT_NPPPD_CTL_MAX_MSGSZ;
/** Use long line to display information */
int lflag = 0;
static void usage (void);
static void on_exit (void);
static void npppd_who (int);
static void npppd_disconnect (const char *);
static const char *eat_null (const char *);
static void npppd_ctl_common(int);
static void print_who(struct npppd_who *);
static void print_stat(struct npppd_who *);
static const char *progname = NULL;
/** show usage */
static void
usage(void)
{
fprintf(stderr,
"usage: %s [-slnuh] [-d ppp_user] [-r rcvbuf_sz] [-p npppd_ctl_path]\n"
"usage: %s -R\n"
"\t-R: Reset the routing table.\n"
"\t-d: Disconnect specified user.\n"
"\t-h: Show this usage.\n"
"\t-l: Use long line to display information.\n"
"\t-n: Don't convert addresses/ports to names.\n"
"\t-p: Specify the path to the npppd's control socket.\n"
"\t-r: Receive buffer size (default %d).\n"
"\t-s: Show statistics informations instead of who.\n"
"\t-u: Show 'since' field as unix time.\n",
progname, progname, rcvbuf_sz);
}
static void
on_signal(int notused)
{
exit(1);
}
/** entry point of 'npppdctl' command */
int
main(int argc, char *argv[])
{
int ch, sflag, fdgramsock, rtflag;
const char *path = DEFAULT_NPPPD_CTL_SOCK_PATH;
const char *disconn;
struct sockaddr_un sun;
struct timeval tv;
extern int optind;
progname = basename(argv[0]);
disconn = NULL;
sflag = rtflag = 0;
while ((ch = getopt(argc, argv, "ld:sunhp:r:R")) != -1) {
switch (ch) {
case 'n':
nflag = 1;
break;
case 'd':
disconn = optarg;
break;
case 's':
sflag = 1;
break;
case 'u':
uflag = 1;
break;
case 'l':
lflag = 1;
break;
case 'r':
if (sscanf(optarg, "%d", &rcvbuf_sz) != 1) {
fprintf(stderr, "Invalid argument: %s\n",
optarg);
exit(1);
}
break;
case 'p':
path = optarg;
break;
case 'R':
rtflag = 1;
break;
case 'h':
case '?':
default:
usage();
exit(1);
}
}
argc -= optind;
argv += optind;
/* create a listening socket for unix domain datagram. */
if ((fdgramsock = mkstemp(dgramsock)) < 0)
err(1, "mkstemp");
/* set the hook that deletes the listening socket on exiting */
if (atexit(on_exit) != 0)
err(1, "atexit");
signal(SIGINT, on_signal);
signal(SIGHUP, on_signal);
signal(SIGTERM, on_signal);
if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
err(1, "socket");
tv.tv_sec = DEFAULT_TIMEOUT;
tv.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbuf_sz,
sizeof(rcvbuf_sz)) < 0)
err(1, "setsockopt(SO_RCVBUF)");
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0)
err(1, "setsockopt(SO_RCVTIMEO)");
/* Prepare a listening socket */
memset(&sun, 0, sizeof(sun));
sun.sun_family = AF_UNIX;
sun.sun_len = sizeof(sun);
strlcpy(sun.sun_path, dgramsock, sizeof(sun.sun_path));
/* delete the file that is created by mkstemp */
close(fdgramsock); unlink(dgramsock);
if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) != 0)
err(1, "bind()");
if (chmod(dgramsock, 0600) != 0)
err(1, "chmod(%s,%d)", dgramsock, 0600);
/* Prepare a socket for sending to the daemon */
memset(&peersock, 0, sizeof(peersock));
peersock.sun_family = AF_UNIX;
peersock.sun_len = sizeof(peersock);
strlcpy(peersock.sun_path, path, sizeof(peersock.sun_path));
if (disconn != NULL)
npppd_disconnect(disconn);
else if (sflag > 0)
npppd_who(1);
else if (rtflag)
npppd_ctl_common(NPPPD_CTL_CMD_RESET_ROUTING_TABLE);
else
npppd_who(0);
close(sock);
sock = -1;
return 0;
}
/** exiting hook. delete the listening socket */
static void
on_exit(void)
{
if (sock >= 0)
close(sock);
unlink(dgramsock);
}
/**
* This function displays connected ppp link one by one.
name assigned since proto from
username 10.0.0.116 Aug 02 21:10 L2TP 192.168.159.103:1701
*/
static void
npppd_who(int show_stat)
{
int i, n, sz, command;
struct npppd_who_list *l;
if ((l = malloc(rcvbuf_sz)) == NULL)
err(1, "malloc(%d)", rcvbuf_sz);
command = NPPPD_CTL_CMD_WHO;
if (sendto(sock, &command, sizeof(command), 0,
(struct sockaddr *)&peersock, sizeof(peersock)) < 0)
err(1 ,"sendto() failed");
if (!show_stat)
printf("name assigned since proto "
"from\n");
else
printf("id name in(Kbytes/pkts/errs)"
" out(Kbytes/pkts/errs)\n");
n = 0;
l->count = -1;
do {
if ((sz = recv(sock, l, rcvbuf_sz, 0)) <= 0)
break;
for (i = 0; n < l->count &&
offsetof(struct npppd_who_list, entry[i + 1]) <= sz;
i++, n++) {
if (show_stat)
print_stat(&l->entry[i]);
else
print_who(&l->entry[i]);
}
} while (i < l->count);
if (l->count >= 0) {
if (n < l->count)
warn("Warning: received message is truncated. "
"Received %d user informations, but %d users are "
"active.", n, l->count);
} else {
warn("recv");
}
free(l);
}
static void
print_who(struct npppd_who *w)
{
int niflags, hasserv;
char assign_ip4buf[16];
char timestr[64], hoststr[NI_MAXHOST], servstr[NI_MAXSERV];
struct sockaddr *sa;
if(nflag)
niflags = NI_NUMERICHOST;
else
niflags = 0;
strlcpy(assign_ip4buf, inet_ntoa(w->assign_ip4), sizeof(assign_ip4buf));
if(!uflag)
strftime(timestr, sizeof(timestr), "%b %d %H:%M",
localtime(&w->time));
else
snprintf(timestr, sizeof(timestr), "%ld", (long)w->time);
sa = (struct sockaddr *)&w->phy_info;
hasserv = (sa->sa_family == AF_INET || sa->sa_family ==AF_INET6)? 1 : 0;
if (sa->sa_family == AF_LINK) {
snprintf(hoststr, sizeof(hoststr),
"%02x:%02x:%02x:%02x:%02x:%02x",
LLADDR((struct sockaddr_dl *)sa)[0] & 0xff,
LLADDR((struct sockaddr_dl *)sa)[1] & 0xff,
LLADDR((struct sockaddr_dl *)sa)[2] & 0xff,
LLADDR((struct sockaddr_dl *)sa)[3] & 0xff,
LLADDR((struct sockaddr_dl *)sa)[4] & 0xff,
LLADDR((struct sockaddr_dl *)sa)[5] & 0xff);
} else if (sa->sa_family < AF_MAX) {
getnameinfo((const struct sockaddr *)&w->phy_info,
sa->sa_len, hoststr, sizeof(hoststr), servstr,
sizeof(servstr),
niflags | ((hasserv)? NI_NUMERICSERV :0));
} else if (sa->sa_family == NPPPD_AF_PHONE_NUMBER) {
strlcpy(hoststr, ((npppd_phone_number *)sa)->pn_number,
sizeof(hoststr));
} else {
strlcpy(hoststr, "error", sizeof(hoststr));
}
if (hasserv)
printf((lflag)
? "%-15s %-15s %-12s %-6s %s:%s\n"
: "%-15.15s %-15s %-12s %-6s %s:%s\n",
eat_null(w->name), assign_ip4buf, timestr, w->phy_label,
hoststr, servstr);
else
printf((lflag)
? "%-15s %-15s %-12s %-6s %s\n"
: "%-15.15s %-15s %-12s %-6s %s\n",
eat_null(w->name), assign_ip4buf, timestr, w->phy_label,
hoststr);
}
static void
print_stat(struct npppd_who *w)
{
printf((lflag)
? "%7d %-20s %9.1f %7u %5u %9.1f %7u %5u\n"
: "%7d %-20.20s %9.1f %7u %5u %9.1f %7u %5u\n", w->id,
eat_null(w->name), (double)w->ibytes/1024, w->ipackets, w->ierrors,
(double)w->obytes/1024, w->opackets, w->oerrors);
}
/** disconnect by username */
static void
npppd_disconnect(const char *username)
{
int sz, len;
u_char buf[BUFSIZ];
struct npppd_disconnect_user_req *req;
req = (struct npppd_disconnect_user_req *)buf;
req->command = NPPPD_CTL_CMD_DISCONNECT_USER;
len = sizeof(struct npppd_disconnect_user_req);
len += strlcpy(req->username, username, sizeof(req->username));
if (sendto(sock, buf, sizeof(struct npppd_disconnect_user_req), 0,
(struct sockaddr *)&peersock, sizeof(peersock)) < 0)
err(1, "sendto");
if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0)
err(1, "recv");
buf[sz] = '\0';
printf("%s\n", buf);
}
/**
* make sure "str" is not NULL or not zero length string.
*
* When NULL pointer or zero length string is specified as "str", this function
* returns "". Otherwise it returns the "str" without modification.
*/
static const char *
eat_null(const char *str)
{
if (str == NULL || *str == '\0')
return "";
return str;
}
static void
npppd_ctl_common(int command)
{
int sz;
u_char buf[BUFSIZ];
if (sendto(sock, &command, sizeof(command), 0,
(struct sockaddr *)&peersock, sizeof(peersock)) < 0) {
err(1 ,"sendto() failed");
}
if ((sz = recv(sock, buf, sizeof(buf), 0)) <= 0)
err(1, "recv");
buf[sz] = '\0';
printf("%s\n", buf);
}