Skip to content

Commit

Permalink
add option -f
Browse files Browse the repository at this point in the history
Specifying the target MAC addresses each time is tedious and prone to
mistakes.  The method of using database files to look up MAC addresses
solves this problem.
  • Loading branch information
KusaReMKN committed Mar 31, 2022
1 parent 9c1bff0 commit d34939c
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 10 deletions.
5 changes: 3 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.69])
AC_INIT([sendwol], [0.02], [https://github.com/KusaReMKN/sendwol/issue])
AC_INIT([sendwol], [0.10], [https://github.com/KusaReMKN/sendwol/issue])
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_SRCDIR([sendwol.c])
AC_CONFIG_HEADERS([config.h])
Expand All @@ -13,7 +13,7 @@ AC_PROG_CC
# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h locale.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h unistd.h])
AC_CHECK_HEADERS([arpa/inet.h locale.h netdb.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_C_RESTRICT
Expand All @@ -22,6 +22,7 @@ AC_TYPE_UINT16_T
AC_TYPE_UINT8_T

# Checks for library functions.
AC_FUNC_MALLOC
AC_CHECK_FUNCS([memset setlocale socket strtol])

AC_CONFIG_FILES([Makefile])
Expand Down
36 changes: 35 additions & 1 deletion sendwol.1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.\" Copyright (c) 2022 KusaReMKN
.\" Available under the 2-Clause BSD License
.\"
.Dd 28th March 2022
.Dd 30th March 2022
.Dt SENDWOL 1
.Os
.Sh NAME
Expand All @@ -12,6 +12,7 @@
.Nm
.Op Fl 4 | Fl 6
.Op Fl b | Fl a Ar address | Fl h Ar hostname
.Op Fl f Ar file
.Op Fl i Ar interface
.Op Fl p Ar port | Fl s Ar service
.Ar target ...
Expand All @@ -36,6 +37,26 @@ Use the broadcast address of the interface as destination.
This is the default.
.It Fl a Ar address , Fl h Ar hostname
Specify the destination address or host name.
.It Fl f Ar file
Specify the database file containing the MAC addresses of the network hosts.
The data is stored in the following format:
.Bd -literal -offset indent
.Ar MAC-address host-name
.Ed
.Pp
Each element is separated by any number of blank spaces and/or tab characters.
If there is a hash character
.Pq Ql #
at the beginning of a line, the line is ignored as a comment.
.Pp
By default,
.Pa $HOME/.sendwol
and
.Pa /etc/ethers
are looked up in this order.
The specified file is looked up before them.
If this option is specified more than once,
those specified later will be looked up first.
.It Fl i Ar interface
Specify the name of the interface from which packets are sent.
This option applies only if the UDP destination is a multicast address
Expand All @@ -47,6 +68,18 @@ Specify the destination port number or service name.
The default value is
.Ql 9 .
.El
.Sh ENVIRONMENT
.Bl -tag -compact
.It Ev HOME
Pathname of the user's home directory.
.El
.Sh FILES
.Bl -tag -compact
.It Pa $HOME/.sendwol
User's MAC address database file.
.It Pa /etc/ethers
System Ethernet address database file.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
Expand Down Expand Up @@ -76,6 +109,7 @@ and the packets would reach every node on the network link.
.Xr ip 4 ,
.Xr ip6 4 ,
.Xr udp 4 ,
.Xr ethers 5 ,
.Xr ifconfig 8
.Sh COPYRIGHT
Copyright \(co 2022
Expand Down
142 changes: 135 additions & 7 deletions sendwol.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/ioctl.h>

#include <net/if.h>
Expand Down Expand Up @@ -61,12 +62,25 @@ struct magic_packet {
struct macaddr mp_target[MP_TARGET_REP];
} __attribute__((__packed__));

/*
* database list
*/
struct dblist {
struct dblist *dbl_next;
const char *dbl_path;
};

static int brd_connect(int domain, const char *restrict servname,
const char *restrict interface);
static struct magic_packet *build_magicpacket(const struct macaddr *target);
static void free_dblist(struct dblist *head);
static char *home_sendwol(void);
static struct macaddr *lookup_dblist(const struct dblist *restrict head,
const char *restrict hostname);
static struct macaddr *mac_aton(const char *a);
static const char *multicast_if(int fd, int domain, const char *interface);
static int push_dblist(struct dblist **restrict headp,
const char *restrict path);
static int udp_connect(int domain, const char *restrict nodename,
const char *restrict servname, const char *restrict interface);
static void usage(void);
Expand All @@ -76,20 +90,38 @@ main(int argc, char *argv[])
{
int c, fd, yes;
int broadcast, domain, forcev4, forcev6;
char *interface, *nodename, *servname;
char *homesendwol, *interface, *nodename, *servname;
struct dblist *list;
struct macaddr *target;
struct magic_packet *packet;
ssize_t written;

(void)setlocale(LC_ALL, "");

list = NULL;
if (push_dblist(&list, "/etc/ethers") == -1)
goto dberror;

homesendwol = home_sendwol();
if (homesendwol != NULL) {
if (push_dblist(&list, homesendwol) == -1) {
dberror: warn(NULL);
warnx("database files are not available");
free_dblist(list);
list = NULL;
}
} else {
warn(NULL);
warnx("~/.sendwol is not available");
}

forcev4 = 0;
forcev6 = 0;
broadcast = 0;
interface = NULL;
nodename = NULL;
servname = "9";
while ((c = getopt(argc, argv, "46ba:h:i:p:s:")) != -1)
while ((c = getopt(argc, argv, "46ba:h:f:i:p:s:")) != -1)
switch (c) {
case '4':
forcev4 = 1;
Expand All @@ -104,6 +136,16 @@ main(int argc, char *argv[])
case 'h':
nodename = optarg;
break;
case 'f':
if (list == NULL)
break;
if (push_dblist(&list, optarg) == -1) {
warn(NULL);
warnx("database files are not available");
free_dblist(list);
list = NULL;
}
break;
case 'i':
interface = optarg;
break;
Expand Down Expand Up @@ -141,15 +183,21 @@ main(int argc, char *argv[])
for (; *argv != NULL; argv++) {
target = mac_aton(*argv);
if (target == NULL) {
warnx("%s: invalid MAC address", *argv);
continue;
target = lookup_dblist(list, *argv);
if (target == NULL) {
warnx("%s: invalid MAC address"
" or unknown host", *argv);
continue;
}
}
packet = build_magicpacket(target);
written = write(fd, packet, sizeof(*packet));
if (written != sizeof(*packet))
err(1, "write");
}

free_dblist(list);

if (close(fd) == -1)
err(1, "close");

Expand Down Expand Up @@ -236,6 +284,70 @@ build_magicpacket(const struct macaddr *target)
return &mp;
}

static void
free_dblist(struct dblist *head)
{
struct dblist *tmp;

while ((tmp = head) != NULL) {
head = head->dbl_next;
free(tmp);
}
}

static char *
home_sendwol(void)
{
#define HOME_SENDWOL "%s/.sendwol"
int length;
char *buffer, *home;

home = getenv("HOME");
if (home == NULL)
return NULL;

length = snprintf(NULL, 0, HOME_SENDWOL, home) + 1;
buffer = (char *)malloc(length);
if (buffer == NULL)
return NULL;

snprintf(buffer, length, HOME_SENDWOL, home);

return buffer;
}

static struct macaddr *
lookup_dblist(const struct dblist *restrict head,
const char *restrict hostname)
{
int elem;
FILE *fp;
struct macaddr *res;
char line[BUFSIZ]; /* I have no idea how much buffer is needed */
char host[MAXHOSTNAMELEN]; /* This is also */
char addrstr[18];

for (; head != NULL; head = head->dbl_next) {
fp = fopen(head->dbl_path, "r");
if (fp == NULL)
continue;
while (fgets(line, sizeof(line), fp) != NULL) {
if (line[0] == '#')
continue;
elem = sscanf(line, "%s%s", addrstr, host);
if (elem < 2)
continue;
if (strcmp(hostname, host) == 0) {
(void)fclose(fp);
return mac_aton(addrstr); /* found */
}
}
(void)fclose(fp);
}

return NULL;
}

static struct macaddr *
mac_aton(const char *a)
{
Expand Down Expand Up @@ -285,6 +397,22 @@ multicast_if(int fd, int domain, const char *interface)
return NULL;
}

static int
push_dblist(struct dblist **restrict headp, const char *restrict path)
{
struct dblist *temp;

temp = (struct dblist *)malloc(sizeof(*temp));
if (temp == NULL)
return -1;

temp->dbl_next = *headp;
temp->dbl_path = path;
*headp = temp;

return 0;
}

static int
udp_connect(int domain, const char *restrict nodename,
const char *restrict servname, const char *restrict interface)
Expand Down Expand Up @@ -328,13 +456,13 @@ udp_connect(int domain, const char *restrict nodename,
return fd;
}


static void
usage(void)
{
(void)fprintf(stderr,
"usage: sendwol [-4 | -6]"
" [-b | -a address | -h hostname] [-i interface]\n"
" [-p port | -s service] target ...\n");
" [-b | -a address | -h hostname] [-f file]\n"
" [-i interface] [-p port | -s service]"
" target ...\n");
exit(1);
}

0 comments on commit d34939c

Please sign in to comment.