1 /*
2  * wol - wake on lan client
3  *
4  * main program
5  *
6  * $Id: wol.c,v 1.18 2004/04/18 11:42:11 wol Exp $
7  *
8  * Copyright (C) 2000,2001,2002,2003,2004 Thomas Krennwallner <krennwallner@aon.at>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
23  * USA.
24  */
25 
26 
27 
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif /* HAVE_CONFIG_H */
31 
32 #include <stdio.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <getopt.h>
37 #include <errno.h>
38 #include <error.h>
39 
40 #include "wrappers.h"
41 #include "xalloc.h"
42 #include "wol.h"
43 #include "magic.h"
44 #include "net.h"
45 #include "macfile.h"
46 #include "getpass4.h"
47 
48 
49 
50 /* My name is argv[0], used by error() */
51 char *program_name;
52 
53 /* pointer to a MAC address */
54 static char *mac_str = NULL;
55 
56 /* IP Address or hostname magic packet is addressed to */
57 static char *host_str = DEFAULT_IPADDR;
58 
59 /* filename with mac addresses */
60 static char *pathname = NULL;
61 
62 /* udp port */
63 static unsigned int port = DEFAULT_PORT;
64 
65 /* SecureON password */
66 static char *passwd = NULL;
67 
68 /* default is not to read from stdin */
69 static int request_stdin = 0;
70 
71 /* be verbose */
72 static int verbose = 0;
73 
74 /* send proxy packet */
75 static int proxy_mode = 0;
76 
77 /* how long to wait between packets */
78 static int msecs = 0;
79 
80 /* a magic packet */
81 static struct magic *magic = NULL;
82 
83 /* socket file descriptor */
84 static int sockfd = -1;
85 
86 
87 
88 static void
usage(int status)89 usage (int status)
90 {
91   if (status)
92     {
93       fprintf (stderr, _("Try `%s --help' for more information.\n"), program_name);
94     }
95   else
96     {
97       fprintf (stdout, _("\
98 Usage: %s [OPTION] ... MAC-ADDRESS ...\n\
99 Wake On LAN client - wakes up magic packet compliant machines.\n\n\
100     --help          display this help and exit\n\
101 -V, --version       output version information and exit\n\
102 -v, --verbose       verbose output\n\
103 -w, --wait=NUM      wait NUM millisecs after sending\n\
104 -h, --host=HOST     broadcast to this IP address or hostname\n\
105 -i, --ipaddr=HOST   same as --host\n\
106 -p, --port=NUM      broadcast to this UDP port\n\
107 -f, --file=FILE     read addresses from file FILE (\"-\" reads from stdin)\n\
108     --passwd[=PASS] send SecureON password PASS (if no PASS is given, you\n\
109                     will be prompted for the password)\n\
110 \n\
111 Each MAC-ADDRESS is written as x:x:x:x:x:x, where x is a hexadecimal number\n\
112 between 0 and ff which represents one byte of the address, which is in\n\
113 network byte order (big endian).\n"), program_name);
114 
115       fprintf (stdout, _("\n\
116 PASS is written as x-x-x-x-x-x, where x is a hexadecimal number between 0\n\
117 and ff which represents one byte of the password.\n"));
118 
119       fprintf (stdout, _("\nReport bugs to <krennwallner@aon.at>\n"));
120     }
121 
122   exit (status);
123 }
124 
125 
126 
127 static void
version(void)128 version (void)
129 {
130   fprintf (stdout, PACKAGE " " VERSION "\n\n");
131   fprintf (stdout, _("\
132 Copyright (C) 2000-2004 Thomas Krennwallner <krennwallner@aon.at>\n\
133 This is free software; see the source for copying conditions. There is NO\n\
134 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\
135 \n"));
136 }
137 
138 
139 
140 /* parse command line and set various globals */
141 static int
parse_args(int argc,char * argv[])142 parse_args (int argc, char *argv[])
143 {
144   int c;
145   int option_index;
146   int password_set = 0;
147   char *options = "Vvw:h:i:p:f:s:-";
148   static struct option long_options[] =
149     {
150       { "help", no_argument, NULL, 'H' },
151       { "version", no_argument, NULL, 'V' },
152       { "verbose", no_argument, NULL, 'v' },
153       { "wait", required_argument, NULL, 'w' },
154       { "host", required_argument, NULL, 'h' },
155       { "ipaddr", required_argument, NULL, 'i' },
156       { "port", required_argument, NULL, 'p' },
157       { "file", required_argument, NULL, 'f' },
158       { "passwd", optional_argument, NULL, 'P' },
159       /* { "proxy", required_argument, NULL, 's' }, */
160       { NULL, 0, NULL, 0 }
161     };
162 
163 
164   if (argc == 1)
165     {
166       error (0, 0, _("Too few arguments."));
167       usage (1);
168     }
169 
170 
171 
172   for (;;)
173     {
174       c = getopt_long (argc, argv, options, long_options, &option_index);
175       if (c == -1) break;
176 
177       switch (c)
178 	{
179 	case 'H':
180 	  usage (0);
181 	  break;
182 
183 
184 	case 'V':
185 	  version ();
186 	  exit (0);
187 	  break;
188 
189 
190 	case 'v':
191 	  verbose = 1;
192 	  break;
193 
194 
195 	case 'w':
196 	  if (sscanf (optarg, "%u", &msecs) != 1)
197 	    {
198 	      error (0, 0, _("Invalid time given"));
199 	      usage (1);
200 	    }
201 	  msecs *= 1000;
202 	  break;
203 
204 
205 	/* case 's': */
206 	/*   proxy_mode = 1; */
207 	case 'h':
208 	case 'i':
209 	  host_str = optarg;
210 	  break;
211 
212 
213 	case 'p':
214 	  if ((sscanf (optarg, "%5u", &port) != 1) ||
215 	      port > 65535 || port == 0)
216 	    {
217 	      error (0, 0, _("Invalid port given"));
218 	      usage (1);
219 	    }
220 	  break;
221 
222 
223 	case 'f':
224 	  pathname = optarg;
225 	  break;
226 
227 
228 	case 'P':
229 	  if (optarg == NULL)
230 	    {
231 	      size_t n;
232 
233 	      if (password_set)
234 		break;
235 
236 	      if (getpass4 (_("Password"), &passwd, &n, stdin) == -1)
237 		{
238 		  error (1, 0, "getpass4 failed");
239 		}
240 	      password_set = 1;
241 	    }
242 	  else
243 	    {
244 	      passwd = optarg;
245 	    }
246 	  break;
247 
248 
249 	case '?':
250 	  break;
251 	}
252     }
253 
254   if ((optind == argc) && (pathname == NULL))
255     {
256       error (0, 0, _("You must specify at least one MAC-ADDRESS."));
257       usage (1);
258     }
259 
260   /* check if stdin is requested */
261   if (optind < argc)
262     {
263       int i;
264 
265       for (i = optind; i < argc; ++i)
266 	{
267 	  if (argv[i][0] == '-' && argv[i][1] == 0)
268 	    {
269 	      request_stdin = 1;
270 	      break;
271 	    }
272 	}
273     }
274   else if (pathname != NULL)
275     {
276       if (!strncmp (pathname, "-", 1))
277 	{
278 	  request_stdin = 1;
279 	}
280     }
281 
282   /* return the offset of the GNU getopt sorted parameters */
283   return optind;
284 }
285 
286 
287 
288 static int
assemble_and_send(struct magic * m,const char * mac_str,const char * host_str,unsigned int portnum,const char * pass_str,int socketfd)289 assemble_and_send (struct magic *m,
290 		   const char *mac_str,
291 		   const char *host_str,
292 		   unsigned int portnum,
293 		   const char *pass_str,
294 		   int socketfd)
295 {
296   int ret = magic_assemble (m, mac_str, pass_str);
297 
298   switch (ret)
299     {
300     case -1:
301       error (0, errno, _("Cannot assemble magic packet for '%s'"), mac_str);
302       errno = 0;
303       return -1;
304 
305     case -2:
306       error (0, 0, _("Invalid password given for '%s'"), mac_str);
307       errno = 0;
308       return -1;
309     }
310 
311   if (udp_send (socketfd, host_str, portnum, m->packet, m->size))
312     {
313       error (0, errno, _("Cannot send magic packet for '%s' to %s:%d"),
314 	     mac_str, host_str, portnum);
315       errno = 0;
316       return -1;
317     }
318 
319   fprintf (stdout, _("Waking up %s"), mac_str);
320   if (verbose)
321     {
322       fprintf (stdout, _(" with %s:%d"), host_str, portnum);
323     }
324   fprintf (stdout, _("...\n"));
325 
326   if (msecs)
327     {
328       usleep (msecs);
329     }
330 
331   return 0;
332 }
333 
334 
335 
336 int
main(int argc,char * argv[])337 main (int argc, char *argv[])
338 {
339   int i;
340   int ret = 0;
341 
342   /* my name is ... */
343   program_name = argv[0];
344 
345 #if ENABLE_NLS
346   setlocale (LC_ALL, "");
347   bindtextdomain (PACKAGE, LOCALEDIR);
348   textdomain (PACKAGE);
349 #endif /* ENABLE_NLS */
350 
351   i = parse_args (argc, argv);
352 
353   magic = magic_create (passwd != NULL);
354   if (magic == NULL)
355     {
356       exit (1);
357     }
358 
359   if (!proxy_mode)
360     {
361       sockfd = udp_open ();
362     }
363   else
364     {
365       sockfd = tcp_open (host_str, port);
366     }
367 
368   if (sockfd < 0)
369     {
370       exit (1);
371     }
372 
373 
374   /* loop through possible MAC addresses */
375   if (!request_stdin)
376     {
377       if (!proxy_mode)
378 	{
379 	  for (; i < argc; i++)
380 	    {
381 	      ret -= assemble_and_send (magic, argv[i], host_str, port, passwd,	sockfd);
382 	    }
383 	}
384       else
385 	{
386 	  /* FIXME: proxy mode */
387 	}
388     }
389 
390 
391   /* -f given */
392   if (pathname || request_stdin)
393     {
394       FILE *fp;
395 
396       if (request_stdin)
397 	{
398 	  fp = stdin;
399 	}
400       else
401 	{
402 	  fp = fopen (pathname, "r");
403 	  if (fp == NULL)
404 	    {
405 	      error (1, errno, "%s", pathname);
406 	    }
407 	}
408 
409       if (!proxy_mode)
410 	{
411 	  /* loop through fp */
412 	  for (;;)
413 	    {
414 	      if (macfile_parse (fp, &mac_str, &host_str, &port, &passwd)) break;
415 
416 	      if (port == 0 || port > 65535)
417 		{
418 		  port = DEFAULT_PORT;
419 		}
420 
421 	      ret -= assemble_and_send (magic, mac_str, host_str, port, passwd,	sockfd);
422 
423 	      XFREE (mac_str);
424 	      XFREE (host_str);
425 	      XFREE (passwd);
426 	    }
427 	}
428       else
429 	{
430 	  /* FIXME: proxy mode */
431 	}
432 
433       fclose (fp);
434     }
435 
436   net_close (sockfd);
437 
438   magic_destroy (magic);
439 
440   exit (ret != 0);
441 }
442