1 /******************************************************************************
2     (c) 2001-2004 christine Caulfield                 christine.caulfield@googlemail.com
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 ******************************************************************************/
14 #include <sys/types.h>
15 #include <sys/uio.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/ioctl.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <sys/utsname.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <termios.h>
27 #include <signal.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <ctype.h>
32 #include <regex.h>
33 #include <stdlib.h>
34 // #include <utmp.h>
35 #include <grp.h>
36 #include <signal.h>
37 #include <assert.h>
38 #ifdef HAVE_NET_IF_ETHER_H
39 #include <net/if.h>
40 #include <net/if_ether.h>
41 #endif
42 #ifdef HAVE_NET_ETHERNET_H
43 #include <net/ethernet.h>
44 #endif
45 #ifdef HAVE_NETINET_ETHER_H
46 #include <netinet/ether.h>
47 #endif
48 
49 #include <string>
50 
51 #include "interfaces.h"
52 #include "moprc.h"
53 
54 #ifdef __FreeBSD__
55 #define ether_addr_octet octet
56 #endif
57 
58 static int  mop_socket;
59 static unsigned char last_message[1500];
60 static int  last_message_len;
61 static int  show_info = 0;
62 static int  do_moprc(u_int8_t *, int, int, int);
63 static int  send_boot(u_int8_t *macaddr, int interface);
64 static LATinterfaces *iface;
65 
usage(FILE * f,char * cmd)66 static int usage(FILE *f, char *cmd)
67 {
68     fprintf(f, "\nUsage: %s [?hVv] [-i <interface>] <node name>|<macaddr>\n", cmd);
69 
70     fprintf(f, "   -?         Show this usage message\n");
71     fprintf(f, "   -h         Show this usage message\n");
72     fprintf(f, "   -V         Show the version of moprc\n");
73     fprintf(f, "   -i         Ethernet interface to use (default to first found)\n");
74     fprintf(f, "   -t         Trigger (reboot) the server\n");
75     fprintf(f, "   -b         Make ^H send DEL\n");
76     fprintf(f, "   -v         Show target information\n");
77     fprintf(f, "   -p <ms>    Set poll interval (default 200)\n");
78     fprintf(f, "\n");
79     fprintf(f, "Node names are read from /etc/ethers\n");
80     fprintf(f, "MAC addresses in colon-seperated form. eg:\n");
81     fprintf(f, "\n%s -i eth1 08:00:2B:2B:AD:99\n", cmd);
82     if (geteuid() != 0)
83 	fprintf(f, "\nYou will probably need to be root to run this program.\n");
84     fprintf(f, "\n");
85     return -1;
86 }
87 
88 
89 /* Send a MOP message to a specified MAC address */
send_message(unsigned char * buf,int len,int interface,u_int8_t * macaddr)90 static int send_message(unsigned char *buf, int len, int interface, u_int8_t *macaddr)
91 {
92     int status;
93     if (len < 46) len = 46;
94 
95     status = iface->send_packet(interface, macaddr, buf, len);
96     if (status < 0)
97 	    perror("send message");
98 
99     memmove(last_message, buf, len);
100     last_message_len = len;
101     return status;
102 }
103 
send_last_message(int interface,u_int8_t * macaddr)104 static int send_last_message(int interface, u_int8_t *macaddr)
105 {
106     return send_message(last_message, last_message_len, interface, macaddr);
107 }
108 
main(int argc,char * argv[])109 int main(int argc, char *argv[])
110 {
111     int opt;
112     int interface = -1;
113     int trigger=0;
114     int convert_bs = 0;
115     int poll_interval = 200; /* in ms */
116     char ifname_buf[255];
117     char *ifname;
118     struct ether_addr addr;
119 
120     if (argc < 2)
121     {
122         return usage(stdout, argv[0]);
123     }
124 
125     /* Look for MOPRC_INTERFACE environment variable */
126     ifname = getenv("MOPRC_INTERFACE");
127 
128 /* Get command-line options */
129     opterr = 0;
130     while ((opt=getopt(argc,argv,"?hVvtbi:p:")) != EOF)
131     {
132 	switch(opt)
133 	{
134 	case 'h':
135 	    return usage(stdout, argv[0]);
136 
137 	case '?':
138 	    return usage(stdout, argv[0]);
139 
140 	case 'v':
141 	    show_info++;
142 	    break;
143 
144 	case 't':
145 	    trigger++;
146 	    break;
147 
148 	case 'b':
149 	    convert_bs++;
150 	    break;
151 
152 	case 'i':
153 	    strcpy(ifname_buf, optarg);
154 	    ifname = ifname_buf;
155 	    break;
156 
157 	case 'p':
158 	    poll_interval = atoi(optarg);
159 	    break;
160 
161 	case 'V':
162 	    printf("\nMoprc version %s\n\n", VERSION);
163 	    exit(0);
164 	    break;
165 	}
166     }
167 
168     if (!argv[optind])
169     {
170 	return usage(stderr, argv[0]);
171     }
172 
173     /* Check for a hostname in /etc/ethers */
174     if (ether_hostton(argv[optind], &addr) != 0)
175     {
176 #ifndef __APPLE__
177 	struct ether_addr *addr1;
178 
179 	/* Otherwise parse ethernet MAC address */
180 	addr1 = ether_aton(argv[optind]);
181 	if (addr1)
182 	{
183 	    addr = *addr1;
184 	}
185 	else
186 #endif
187 	{
188 	    fprintf(stderr, "unknown node name or bad MAC address %s\n", argv[optind]);
189 	    return 3;
190 	}
191     }
192 
193     /* Initialise the platform-specific interface code */
194     iface = LATinterfaces::Create();
195     if (iface->Start(LATinterfaces::ProtoMOP) == -1)
196     {
197 	fprintf(stderr, "Can't create MOP protocol socket: %s\n", strerror(errno));
198 	exit(1);
199     }
200 
201     // If no interface on the command-line then use defaults
202     interface = iface->find_interface(ifname);
203     if (interface == -1)
204     {
205 	if (ifname)
206 	    fprintf(stderr, "Cannot resolve interface %s\n", ifname);
207 	else
208 	    fprintf(stderr, "Cannot find any ethernet interfaces\n");
209 	return 2;
210     }
211 
212     mop_socket = iface->get_fd(interface);
213     if (iface->bind_socket(interface))
214 	return 3;
215 
216     if (trigger)
217     {
218 	return send_boot(addr.ether_addr_octet, interface);
219     }
220 
221     return do_moprc(addr.ether_addr_octet, interface, convert_bs, poll_interval*1000);
222 }
223 
readmop(unsigned char * buf,int buflen)224 static int readmop(unsigned char *buf, int buflen)
225 {
226     int ifn;
227     unsigned char macaddr[6];
228     bool more;
229 
230     return iface->recv_packet(mop_socket, ifn, macaddr, buf, buflen, more);
231 }
232 
send_reserve(u_int8_t * macaddr,int interface)233 static int send_reserve(u_int8_t *macaddr, int interface)
234 {
235     unsigned char buf[32];
236     memset(buf, 0, sizeof(buf));
237 
238     buf[0] = 9;
239     buf[1] = 0;
240     buf[2] = MOPRC_CMD_RESERVE;
241 
242    return  send_message(buf, 11, interface, macaddr);
243 }
244 
send_release(u_int8_t * macaddr,int interface)245 static int send_release(u_int8_t *macaddr, int interface)
246 {
247     unsigned char buf[32];
248     memset(buf, 0, sizeof(buf));
249 
250     buf[0] = 1;
251     buf[1] = 0;
252     buf[2] = MOPRC_CMD_RELEASE;
253 
254    return  send_message(buf, 3, interface, macaddr);
255 }
256 
send_reqid(u_int8_t * macaddr,int interface)257 static int send_reqid(u_int8_t *macaddr, int interface)
258 {
259     unsigned char buf[32];
260     memset(buf, 0, sizeof(buf));
261 
262     buf[0] = 5;
263     buf[1] = 0;
264     buf[2] = MOPRC_CMD_REQUESTID;
265 
266    return  send_message(buf, 7, interface, macaddr);
267 }
268 
send_boot(u_int8_t * macaddr,int interface)269 static int send_boot(u_int8_t *macaddr, int interface)
270 {
271     unsigned char buf[32];
272     memset(buf, 0, sizeof(buf));
273 
274     buf[0] = 12;
275     buf[1] = 0;
276     buf[2] = MOPRC_CMD_BOOT;
277     buf[13] = 0xFF;
278 
279    return  send_message(buf, 14, interface, macaddr);
280 }
281 
send_data(unsigned char * data,int len,u_int8_t * macaddr,int interface)282 static int send_data(unsigned char *data, int len, u_int8_t *macaddr, int interface)
283 {
284     unsigned char buf[len+46];
285     static char message = 0;
286 
287     memset(buf, 0, sizeof(buf));
288 
289     buf[0] = len+2;
290     buf[1] = 0;
291     buf[2] = MOPRC_CMD_COMMANDPOLL;
292     buf[3] = message;
293     memcpy(buf+4, data, len);
294 
295     message = 1-message;
296     return send_message(buf, len+4, interface, macaddr);
297 }
298 
print_ascic(unsigned char * buf,int len)299 static void print_ascic(unsigned char *buf, int len)
300 {
301     int i;
302 
303     for (i=0; i <len; i++)
304     {
305 	if (isprint(buf[i]))
306 	    printf("%c", buf[i]);
307 	else
308 	    printf(".");
309     }
310     printf("\n");
311 }
312 
show_system_info(unsigned char * info,int len)313 static int show_system_info(unsigned char *info, int len)
314 {
315     int index=0;
316     int functions = 0;
317 
318     while (index < len)
319     {
320 	int type = info[index] | info[index+1]<<8;
321 	int infolen = info[index+2];
322 
323 	index += 3;
324 	if (show_info || type == 2)
325 	{
326 	    switch (type)
327 	    {
328 	    case 1: /* Maintenance version */
329 		printf("Maintenance Version: %d.%d.%d\n",
330 		       info[index],info[index+1],info[index+2]);
331 		break;
332 	    case 2: /* Functions */
333 		functions = info[index];
334 		break;
335 
336 		/* These are sent by terminal servers */
337 	    case 102:
338 		printf("ROM version:         ");
339 		print_ascic(&info[index], infolen);
340 		break;
341 	    case 103:
342 		printf("S/W version:         ");
343 		print_ascic(&info[index], infolen);
344 		break;
345 	    case 105:
346 		printf("Node Name:           ");
347 		print_ascic(&info[index], infolen);
348 		break;
349 	    case 106:
350 		printf("Identification:      ");
351 		print_ascic(&info[index], infolen);
352 		break;
353 
354 		/* Other stuff */
355 	    case 201:
356 		printf("Operating System:    ");
357 		print_ascic(&info[index], infolen);
358 		break;
359 	    case 202:
360 		printf("Software Version:    ");
361 		print_ascic(&info[index], infolen);
362 		break;
363 	    case 203:
364 		printf("Node Name:           ");
365 		print_ascic(&info[index], infolen);
366 		break;
367 
368 	    default:
369 		break;
370 	    }
371 	    index += infolen;
372 	}
373     }
374     if (show_info) printf("\n");
375     return functions & 0x20; /* Do we do CCP? */
376 }
377 
do_moprc(u_int8_t * macaddr,int interface,int convert_bs,int timeout)378 static int do_moprc(u_int8_t *macaddr, int interface, int convert_bs, int timeout)
379 {
380     enum {STARTING, CONNECTED} state=STARTING;
381     fd_set         in;
382     unsigned char  buf[1500];
383     struct timeval tv;
384     struct termios old_term;
385     struct termios new_term;
386     int            len;
387     int            last_msg_tag = 99; /* Dummy */
388     int            status;
389     int            waiting_ack;
390     int            resends = 0;
391     int            termfd = STDIN_FILENO;
392     int            stdin_is_tty;
393     int            last_packet_was_empty=1;
394 
395     tcgetattr(termfd, &old_term);
396     new_term = old_term;
397 
398     /* Set local terminal characteristics */
399     new_term.c_iflag &= ~BRKINT;
400     new_term.c_iflag |= IGNBRK | INLCR;
401     new_term.c_lflag &= ~ISIG;
402     new_term.c_cc[VMIN] = 1;
403     new_term.c_cc[VTIME] = 0;
404     new_term.c_lflag &= ~ICANON;
405     new_term.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
406     tcsetattr(termfd, TCSANOW, &new_term);
407 
408     /* Send connect packets */
409     send_reserve(macaddr, interface);
410     send_reqid(macaddr, interface);
411     waiting_ack = 1;
412 
413     /* Main loop */
414     FD_ZERO(&in);
415     FD_SET(mop_socket, &in);
416     tv.tv_sec  = 5;
417     tv.tv_usec = 0;
418 
419     stdin_is_tty = isatty(STDIN_FILENO);
420 
421     /* Loop for input */
422     while ( (status = select(FD_SETSIZE, &in, NULL, NULL, &tv)) >= 0)
423     {
424 	/* No response after 3 tries */
425 	if (waiting_ack && resends >= 3)
426 	{
427 	    printf("\nTarget does not respond\n");
428 	    break;
429 	}
430 
431 	/* Waiting for an ACK but not got one. Resend the last
432 	   packet */
433 	if (status == 0 && waiting_ack)
434 	{
435 	    send_last_message(interface, macaddr);
436 	    resends++;
437 	    continue;
438 	}
439 
440 	/* No data, poll for any input from the terminal server */
441 	if (status == 0 && !waiting_ack)
442 	{
443 	    unsigned char dummybuf[1];
444 	    send_data(dummybuf, 0, macaddr, interface);
445 	    waiting_ack = 1;
446 	    resends = 0;
447 	}
448 
449 	/* Data from the terminal server */
450 	if (FD_ISSET(mop_socket, &in))
451 	{
452 	    int cmd;
453 	    int datalen;
454 
455 	    len = readmop(buf, sizeof(buf));
456 	    if (len < 0)
457 	    {
458 	        perror("reading from Remote");
459 		return -1;
460 	    }
461 	    if (len == 0) continue;
462 
463 	    waiting_ack = 0;
464 	    cmd = buf[2];
465 	    datalen = buf[0] | buf[1] <<8;
466 
467 	    /* Ignore duplicates */
468 	    if (last_msg_tag == buf[3])
469 		continue;
470 	    last_msg_tag = buf[3];
471 
472 	    switch (cmd)
473 	    {
474 	    case MOPRC_CMD_RESERVE:
475 	    case MOPRC_CMD_RELEASE:
476 	    case MOPRC_CMD_REQUESTID:
477 		fprintf(stderr, "Got unsupported MOPRC function %d\n", cmd);
478 		break;
479 
480 		/* Got some data to display */
481 	    case MOPRC_CMD_RESPONSE:
482 		if (datalen > 2)
483 		{
484 		    fwrite(buf+4, datalen-2, 1, stdout);
485 		    fflush(stdout);
486 		    last_packet_was_empty = 0;
487 		}
488 		else
489 		{
490 		    last_packet_was_empty = 1;
491 		}
492 		break;
493 
494 		/* Response to our REQUESTID message means
495 		   we are connected */
496 	    case MOPRC_CMD_SYSTEMID:
497 		if (state == STARTING)
498 		{
499 		    if (show_system_info(&buf[6], datalen-1) == 0)
500 		    {
501 			printf("target does not support remote console\n");
502 			goto finished;
503 		    }
504 
505 		    if (stdin_is_tty)
506 			printf("Console connected (press CTRL/D when finished)\n");
507 
508 		    state = CONNECTED;
509 		}
510 		break;
511 	    default:
512 		fprintf(stderr, "Got unknown MOPRC function %d\n", cmd);
513 		break;
514 	    }
515 	}
516 
517 	/* Keyboard input */
518 	if (FD_ISSET(STDIN_FILENO, &in))
519 	{
520 	    int i;
521 	    len = read(STDIN_FILENO, buf, stdin_is_tty ? sizeof(buf) : 1);
522 	    if (len < 0)
523 	    {
524 	        perror("reading from stdin");
525 		return -1;
526 	    }
527 	    if (len == 0) break;
528 
529 	    /* Convert LF to CR & check for ^D */
530 	    for (i=0; i<len; i++)
531 	    {
532 		if (buf[i] == 4) goto finished;
533 		if (buf[i] == '\n') buf[i] = '\r';
534 		if (convert_bs && buf[i] == '\b') buf[i] = 127;
535 	    }
536 	    waiting_ack = 1;
537 	    resends = 0;
538 	    send_data(buf, len, macaddr, interface);
539 	}
540 
541 	/* Reset for another select */
542 	tv.tv_usec  = timeout;
543 	tv.tv_sec = 0;
544 
545 	FD_ZERO(&in);
546 	FD_SET(mop_socket, &in);
547 	if (!waiting_ack && last_packet_was_empty) FD_SET(STDIN_FILENO, &in);
548     }
549 
550  finished:
551     /* Send disconnect */
552     send_release(macaddr, interface);
553 
554     tcsetattr(termfd, TCSANOW, &old_term);
555     printf("\n");
556     return 0;
557 }
558