xref: /netbsd/usr.sbin/ldpd/ldp_command.c (revision 6550d01e)
1 /* $NetBSD: ldp_command.c,v 1.4 2010/12/31 11:29:33 kefren Exp $ */
2 
3 /*-
4  * Copyright (c) 2010 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Mihai Chelaru <kefren@NetBSD.org>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <arpa/inet.h>
33 
34 #include <netinet/in.h>
35 #include <netinet/tcp.h>
36 
37 #include <sys/socket.h>
38 #include <sys/queue.h>
39 
40 #include <errno.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "label.h"
48 #include "ldp.h"
49 #include "ldp_command.h"
50 #include "ldp_errors.h"
51 #include "ldp_peer.h"
52 #include "socketops.h"
53 
54 struct com_sock csockets[MAX_COMMAND_SOCKETS];
55 extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time,
56 	min_label, max_label, debug_f, warn_f;
57 
58 #define	writestr(soc, str) write(soc, str, strlen(str))
59 
60 #define	MAXSEND 1024
61 char sendspace[MAXSEND];
62 
63 static int	verify_root_pwd(char *);
64 static void	echo_on(int s);
65 static void	echo_off(int s);
66 
67 static struct com_func main_commands[] = {
68 	{ "show", show_func },
69 	{ "set", set_func },
70 	{ "quit", exit_func },
71 	{ "exit", exit_func },
72 	{ "", NULL }
73 };
74 
75 static struct com_func show_commands[] = {
76 	{ "neighbours", show_neighbours },
77 	{ "bindings", show_bindings },
78 	{ "debug", show_debug },
79 	{ "hellos", show_hellos },
80 	{ "parameters", show_parameters },
81 	{ "version", show_version },
82 	{ "warning", show_warning },
83 	{ "", NULL }
84 };
85 
86 struct com_func set_commands[] = {
87 	{ "debug", set_debug },
88 	{ "hello-time", set_hello_time },
89 	{ "warning", set_warning },
90 	{ "", NULL }
91 };
92 
93 int
94 verify_root_pwd(char *pw)
95 {
96 	struct passwd *p;
97 
98 	if ((p = getpwuid(0)) == NULL)
99 		return 0;
100 
101 	if (strcmp(crypt(pw, p->pw_passwd), p->pw_passwd))
102 		return 0;
103 
104 	return 1;
105 }
106 
107 
108 void
109 init_command_sockets()
110 {
111 	int i;
112 
113 	for (i = 0; i<MAX_COMMAND_SOCKETS; i++) {
114 		csockets[i].socket = -1;
115 		csockets[i].auth = 0;
116 	}
117 }
118 
119 int
120 create_command_socket(int port)
121 {
122 	struct sockaddr_in sin;
123 	int s;
124 
125 	sin.sin_len = sizeof(sin);
126 	sin.sin_family = AF_INET;
127 	sin.sin_port = htons(port);
128 	sin.sin_addr.s_addr = ntohl(INADDR_LOOPBACK);
129 
130 	s = socket(PF_INET, SOCK_STREAM, 6);
131 	if (s < 0)
132 		return s;
133 
134 	if (bind(s, (struct sockaddr *) &sin, sizeof(sin))) {
135 		fatalp("bind: %s", strerror(errno));
136 		close(s);
137 		return -1;
138 	}
139 
140 	if (listen(s, 5) == -1) {
141 		fatalp("listen: %s", strerror(errno));
142 		close(s);
143 		return -1;
144 	}
145 	return s;
146 }
147 
148 void
149 command_accept(int s)
150 {
151 	int as = accept(s, NULL, 0);
152 
153 	if (as < 0) {
154 		fatalp("Cannot accept new command socket %s",
155 		    strerror(errno));
156 		return;
157 	}
158 
159 	if (add_command_socket(as) != 0) {
160 		fatalp("Cannot accept command. Too many connections\n");
161 		close(as);
162 		return;
163 	}
164 
165 	/* auth */
166 	send_pwd_prompt(as);
167 }
168 
169 struct com_sock *
170 is_command_socket(int s)
171 {
172 	int i;
173 
174 	if (s == -1)
175 		return NULL;
176 	for (i=0; i<MAX_COMMAND_SOCKETS; i++)
177 		if (s == csockets[i].socket)
178 			return &csockets[i];
179 	return NULL;
180 }
181 
182 int
183 add_command_socket(int s)
184 {
185 	int i;
186 
187 	for (i=0; i<MAX_COMMAND_SOCKETS; i++)
188 		if (csockets[i].socket == -1) {
189 			csockets[i].socket = s;
190 			csockets[i].auth = 0;
191 			return 0;
192 		}
193 	return -1;
194 }
195 
196 void
197 command_dispatch(struct com_sock *cs)
198 {
199 	char recvspace[MAX_COMMAND_SIZE + 1];
200 	char *nextc = recvspace;
201 	int r = recv(cs->socket, recvspace, MAX_COMMAND_SIZE, MSG_PEEK);
202 
203 	if (r < 0) {
204 		command_close(cs->socket);
205 		return;
206 	}
207 
208 	recv(cs->socket, recvspace, r, MSG_WAITALL);
209 
210 	if (r < 3) { /*at least \r\n */
211 	    if (cs->auth) {
212 		/*writestr(cs->socket, "Unknown command. Use ? for help\n");*/
213 		send_prompt(cs->socket);
214 	    } else {
215 		writestr(cs->socket, "Bad password\n");
216 		command_close(cs->socket);
217 	    }
218 		return;
219 	}
220 
221 	recvspace[r - 2] = '\0';
222 
223 	if (!cs->auth) {
224 		if (verify_root_pwd(recvspace)) {
225 			echo_on(cs->socket);
226 			cs->auth = 1;
227 			writestr(cs->socket, "\n");
228 			send_prompt(cs->socket);
229 		} else {
230 			echo_on(cs->socket);
231 			writestr(cs->socket, "Bad password\n");
232 			command_close(cs->socket);
233 		}
234 		return;
235 	}
236 
237 	strsep(&nextc, " ");
238 
239 	command_match(main_commands, cs->socket, recvspace, nextc);
240 
241 }
242 
243 void
244 command_close(int s)
245 {
246 	int i;
247 
248 	for (i=0; i<MAX_COMMAND_SOCKETS; i++)
249 		if (s == csockets[i].socket) {
250 			close(s);
251 			csockets[i].socket = -1;
252 			csockets[i].auth = 0;
253 			break;
254 		}
255 }
256 
257 void
258 send_prompt(int s) {
259 	writestr(s, "LDP> ");
260 }
261 
262 void
263 send_pwd_prompt(int s) {
264 	echo_off(s);
265 	writestr(s, "Password: ");
266 }
267 
268 static void echo_off(int s)
269 {
270 	char iac_will_echo[3] = { 0xff, 0xfb, 0x01 }, bf[32];
271 	write(s, iac_will_echo, sizeof(iac_will_echo));
272 	read(s, bf, sizeof(bf));
273 }
274 
275 static void echo_on(int s)
276 {
277 	char iac_wont_echo[3] = { 0xff, 0xfc, 0x01 }, bf[32];
278 	write(s, iac_wont_echo, sizeof(iac_wont_echo));
279 	read(s, bf, sizeof(bf));
280 }
281 
282 /*
283  * Matching function
284  * Returns 1 if matched anything
285  */
286 int
287 command_match(struct com_func *cf, int s, char *orig, char *next)
288 {
289 	size_t i, len;
290 	int last_match = -1;
291 	const char *msg = NULL;
292 
293 	if (orig == NULL || orig[0] == '\0')
294 		goto out;
295 
296 	if (!strcmp(orig, "?")) {
297 		for (i = 0; cf[i].func != NULL; i++) {
298 			snprintf(sendspace, MAXSEND, "\t%s\n", cf[i].com);
299 			writestr(s, sendspace);
300 		}
301 		goto out;
302 	}
303 
304 	len = strlen(orig);
305 	for (i = 0; cf[i].func != NULL; i++) {
306 		if (strncasecmp(orig, cf[i].com, len) == 0) {
307 			if (last_match != -1) {
308 				msg = "Ambiguous";
309 				goto out;
310 			} else
311 				last_match = i;
312 		}
313 	}
314 
315 	if (last_match == -1) {
316 		msg = "Unknown";
317 		goto out;
318 	}
319 
320 	if (cf[last_match].func(s, next) != 0)
321 		send_prompt(s);
322 	return 1;
323 out:
324 	if (msg) {
325 		writestr(s, msg);
326 		writestr(s, " command. Use ? for help\n");
327 	}
328 	send_prompt(s);
329 	return 0;
330 }
331 
332 /*
333  * Main CLI functions
334  */
335 int
336 set_func(int s, char *recvspace)
337 {
338 	char *nextc = recvspace;
339 
340 	if (recvspace == NULL || recvspace[0] == '\0') {
341 		writestr(s, "Unknown set command. Use set ? for help\n");
342 		return 1;
343 	}
344 
345 	strsep(&nextc, " ");
346 
347 	command_match(set_commands, s, recvspace, nextc);
348 	return 0;
349 }
350 
351 int
352 show_func(int s, char *recvspace)
353 {
354 	char *nextc = recvspace;
355 
356 	if (recvspace == NULL || recvspace[0] == '\0') {
357 		writestr(s, "Unknown show command. Use show ? for help\n");
358 		return 1;
359 	}
360 
361 	strsep(&nextc, " ");
362 
363 	command_match(show_commands, s, recvspace, nextc);
364 	return 0;
365 }
366 
367 int
368 exit_func(int s, char *recvspace)
369 {
370 	command_close(s);
371 	return 0;
372 }
373 
374 /*
375  * Show functions
376  */
377 int
378 show_neighbours(int s, char *recvspace)
379 {
380 	struct ldp_peer *p;
381 	struct ldp_peer_address *wp;
382 	struct sockaddr_in ssin;
383 	socklen_t sin_len = sizeof(struct sockaddr_in);
384 	int enc;
385 	socklen_t enclen = sizeof(enc);
386 
387 	SLIST_FOREACH(p, &ldp_peer_head, peers) {
388 		snprintf(sendspace, MAXSEND, "LDP peer: %s\n",
389 		    inet_ntoa(p->ldp_id));
390 		writestr(s, sendspace);
391 		snprintf(sendspace, MAXSEND, "Transport address: %s\n",
392 		    inet_ntoa(p->transport_address));
393 		writestr(s, sendspace);
394 		snprintf(sendspace, MAXSEND, "Next-hop address: %s\n",
395 		    inet_ntoa(p->address));
396 		writestr(s, sendspace);
397 		snprintf(sendspace, MAXSEND, "State: %s\n",
398 		    ldp_state_to_name(p->state));
399 		writestr(s, sendspace);
400 		if (p->state == LDP_PEER_ESTABLISHED) {
401 			snprintf(sendspace, MAXSEND, "Since: %s",
402 			    ctime(&p->established_t));
403 			writestr(s, sendspace);
404 		}
405 		snprintf(sendspace, MAXSEND, "Holdtime: %d\nTimeout: %d\n",
406 			p->holdtime, p->timeout);
407 		writestr(s, sendspace);
408 
409 		switch(p->state) {
410 		    case LDP_PEER_CONNECTING:
411 		    case LDP_PEER_CONNECTED:
412 		    case LDP_PEER_ESTABLISHED:
413 			if (getsockname(p->socket,(struct sockaddr *) &ssin,
414 			    &sin_len))
415 				break;
416 
417 			if (getsockopt(p->socket, IPPROTO_TCP, TCP_MD5SIG,
418 			    &enc, &enclen) == 0) {
419 				snprintf(sendspace, MAXSEND,
420 				    "Authenticated: %s\n",
421 				    enc != 0 ? "YES" : "NO");
422 				writestr(s, sendspace);
423 			}
424 
425 			snprintf(sendspace, MAXSEND,"Socket: %d\nLocal %s:%d\n",
426 			    p->socket, inet_ntoa(ssin.sin_addr),
427 			    ntohs(ssin.sin_port));
428 			writestr(s, sendspace);
429 
430 			if (getpeername(p->socket,(struct sockaddr *) &ssin,
431 			    &sin_len))
432 				break;
433 			snprintf(sendspace, MAXSEND, "Remote %s:%d\n",
434 				inet_ntoa(ssin.sin_addr), ntohs(ssin.sin_port));
435 			writestr(s, sendspace);
436 		}
437 
438 		snprintf(sendspace, MAXSEND,"Addresses bounded to this peer: ");
439 		writestr(s, sendspace);
440 		SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) {
441 			snprintf(sendspace, MAXSEND, "%s ",
442 			    inet_ntoa(wp->address));
443 			writestr(s, sendspace);
444 		}
445 		sendspace[0] = sendspace[1] = '\n';
446 		write(s, sendspace, 2);
447 	}
448 	return 1;
449 }
450 
451 int
452 show_bindings(int s, char *recvspace)
453 {
454 	struct label *l;
455 
456 	snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n");
457 	writestr(s, sendspace);
458 	SLIST_FOREACH (l, &label_head, labels) {
459 		snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding,
460 		    union_ntoa(&l->so_dest));
461 		writestr(s, sendspace);
462 		snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref));
463 		writestr(s, sendspace);
464 		if (l->p)
465 			snprintf(sendspace, MAXSEND, "\t%s:%d\n",
466 			    inet_ntoa(l->p->address), l->label);
467 		else
468 			snprintf(sendspace, MAXSEND, "\n");
469 		writestr(s, sendspace);
470 	}
471 	return 1;
472 }
473 
474 int
475 show_debug(int s, char *recvspace)
476 {
477 	if (recvspace) {
478 		writestr(s, "Invalid command\n");
479 		return 1;
480 	}
481 
482 	snprintf(sendspace, MAXSEND, "Debug: %s\n",
483 		debug_f ? "YES" : "NO");
484 	writestr(s, sendspace);
485 	return 1;
486 }
487 
488 int
489 show_hellos(int s, char *recvspace)
490 {
491 	struct hello_info *hi;
492 
493 	SLIST_FOREACH(hi, &hello_info_head, infos) {
494 		snprintf(sendspace, MAXSEND, "%s: %ds\n", inet_ntoa(hi->ldp_id),
495 		    hi->keepalive);
496 		writestr(s, sendspace);
497 	}
498 	return 1;
499 }
500 
501 int
502 show_parameters(int s, char *recvspace)
503 {
504 	snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n"
505 		 "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n"
506 		 "Minimum label: %d\nMaximum label: %d\n",
507 		my_ldp_id,
508 		LDP_VERSION,
509 		ldp_hello_time,
510 		ldp_keepalive_time,
511 		ldp_holddown_time,
512 		min_label,
513 		max_label);
514 	writestr(s, sendspace);
515 	return 1;
516 }
517 
518 int
519 show_version(int s, char *recvspace)
520 {
521 	if (recvspace) {	/* Nothing more after this */
522 		writestr(s, "Invalid command\n");
523 		return 1;
524 	}
525 
526 	snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n",
527 	    LDPD_VER);
528 	writestr(s, sendspace);
529 	return 1;
530 }
531 
532 int
533 show_warning(int s, char *recvspace)
534 {
535 	if (recvspace) {
536 		writestr(s, "Invalid command\n");
537 		return 1;
538 	}
539 
540 	snprintf(sendspace, MAXSEND, "Warnings: %s\n",
541 		warn_f ? "YES" : "NO");
542 	writestr(s, sendspace);
543 	return 1;
544 }
545 
546 /* Set commands */
547 int
548 set_hello_time(int s, char *recvspace)
549 {
550 	if (!recvspace || atoi(recvspace) < 1) {
551 		writestr(s, "Invalid timeout\n");
552 		return 1;
553 	}
554 
555 	ldp_hello_time = atoi(recvspace);
556 	return 1;
557 }
558 
559 int
560 set_debug(int s, char *recvspace)
561 {
562 	if (!recvspace || atoi(recvspace) < 0) {
563 		writestr(s, "Invalid command\n");
564 		return 1;
565 	}
566 
567 	debug_f = atoi(recvspace);
568 	return 1;
569 }
570 
571 int
572 set_warning(int s, char *recvspace)
573 {
574 	if (!recvspace || atoi(recvspace) < 0) {
575 		writestr(s, "Invalid command\n");
576 		return 1;
577 	}
578 
579 	warn_f = atoi(recvspace);
580 	return 1;
581 }
582