tmcd.c 95.6 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2003 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
5
6
 * All rights reserved.
 */

7
8
9
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
10
#include <arpa/inet.h>
11
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
12
#include <ctype.h>
13
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
14
15
16
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
17
18
19
20
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
21
#include <sys/wait.h>
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22
#include <sys/fcntl.h>
23
24
#include <sys/syscall.h>
#include <sys/stat.h>
25
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
26
#include <setjmp.h>
27
28
#include <pwd.h>
#include <grp.h>
29
30
#include <mysql/mysql.h>
#include "decls.h"
31
#include "config.h"
32
33
#include "ssl.h"
#include "log.h"
34
#include "tbdefs.h"
35

36
37
38
39
#ifdef EVENTSYS
#include "event.h"
#endif

40
41
42
/*
 * XXX This needs to be localized!
 */
43
44
45
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
46
47
48
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
49
#define PROJDIR		"/proj"
50
#define GROUPDIR	"/groups"
51
#define USERDIR		"/users"
52
#define SHAREDIR	"/share"
53
54
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
Austin Clements's avatar
Austin Clements committed
55
#define FSHOSTID	"/usr/testbed/etc/fshostid"
56
#define DOTSFS		".sfs"
57

58
59
60
#define TESTMODE
#define NETMASK		"255.255.255.0"

61
62
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
63
#define HOSTID_SIZE	(32+64)
64
65
#define DEFAULT_DBNAME	TBDBNAME

66
int		debug = 0;
67
static int	insecure = 0;
68
static char     dbname[DBNAME_SIZE];
69
static struct in_addr myipaddr;
Austin Clements's avatar
Austin Clements committed
70
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
71
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
72
static int	nodeidtocontrolnet(char *nodeid, int *net);
73
static int	vnodetophysnode(char *nodeid, char *physnode);
74
static int	checkprivkey(struct in_addr, char *);
75
76
77
static void	tcpserver(int sock);
static void	udpserver(int sock);
static int      handle_request(int, struct sockaddr_in *, char *, int);
78
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
79
80
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
81
82
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
83
static int	safesymlink(char *name1, char *name2);
84

85
/* thread support */
86
#define MAXCHILDREN	20
87
88
#define MINCHILDREN	5
static int	numchildren;
89
static int	maxchildren = 10;
90
static volatile int killme;
91

92
93
94
95
96
97
98
99
100
/*
 * This structure is passed to each request function. The intent is to
 * reduce the number of DB queries per request to a minimum.
 */
typedef struct {
	int		allocated;
	int		jailflag;
	int		isvnode;
	int		islocal;
101
	int		update_accounts;
102
103
104
105
106
107
108
109
110
111
112
113
114
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
	char		pid[TBDB_FLEN_PID];
	char		eid[TBDB_FLEN_EID];
	char		gid[TBDB_FLEN_GID];
	char		nickname[TBDB_FLEN_VNAME];
	char		type[TBDB_FLEN_NODETYPE];
	char		class[TBDB_FLEN_NODECLASS];	
	char		testdb[256];
} tmcdreq_t;
static int	iptonodeid(struct in_addr, tmcdreq_t *);
static int	checkdbredirect(tmcdreq_t *);

115
116
117
118
119
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

120
121
122
/*
 * Commands we support.
 */
123
124
#define COMMAND_PROTOTYPE(x) \
	static int \
125
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
126
127

COMMAND_PROTOTYPE(doreboot);
128
COMMAND_PROTOTYPE(donodeid);
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
COMMAND_PROTOTYPE(dodelay);
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dohostsV2);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
COMMAND_PROTOTYPE(dolog);
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
144
COMMAND_PROTOTYPE(dosfshostid);
145
146
147
148
149
150
151
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
152
COMMAND_PROTOTYPE(dotunnels);
153
COMMAND_PROTOTYPE(dovnodelist);
154
COMMAND_PROTOTYPE(doisalive);
155
COMMAND_PROTOTYPE(doipodinfo);
156
157
158
COMMAND_PROTOTYPE(doatarball);
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
159
COMMAND_PROTOTYPE(doroutelist);
160
COMMAND_PROTOTYPE(dojailconfig);
161
162
163

struct command {
	char	*cmdname;
164
	int    (*func)(int, tmcdreq_t *, char *, int, int);
165
} command_array[] = {
166
	{ "reboot",	doreboot },
167
	{ "nodeid",	donodeid },
168
169
170
171
	{ "status",	dostatus },
	{ "ifconfig",	doifconfig },
	{ "accounts",	doaccounts },
	{ "delay",	dodelay },
172
173
	{ "hostnamesV2",dohostsV2 },	/* This will go away */
	{ "hostnames",	dohosts },
174
	{ "rpms",	dorpms },
175
	{ "deltas",	dodeltas },
176
	{ "tarballs",	dotarballs },
177
	{ "startupcmd",	dostartcmd },
Mike Hibler's avatar
Mike Hibler committed
178
	{ "startstatus",dostartstat }, /* Leave this before "startstat" */
179
	{ "startstat",	dostartstat },
180
181
	{ "readycount", doreadycount },
	{ "ready",	doready },
Mike Hibler's avatar
Mike Hibler committed
182
	{ "log",	dolog },
183
	{ "mounts",	domounts },
Austin Clements's avatar
Austin Clements committed
184
	{ "sfshostid",	dosfshostid },
185
	{ "loadinfo",	doloadinfo},
Robert Ricci's avatar
Robert Ricci committed
186
	{ "reset",	doreset},
187
	{ "routing",	dorouting},
188
	{ "trafgens",	dotrafgens},
189
	{ "nseconfigs",	donseconfigs},
190
	{ "creator",	docreator},
191
	{ "state",	dostate},
192
	{ "tunnels",	dotunnels},
193
	{ "vnodelist",	dovnodelist},
194
	{ "isalive",	doisalive},
195
	{ "ipodinfo",	doipodinfo},
196
197
198
	{ "ntpinfo",	dontpinfo},
	{ "ntpdrift",	dontpdrift},
	{ "tarball",	doatarball},
199
	{ "routelist",	doroutelist},
200
	{ "jailconfig",	dojailconfig},
201
202
203
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

204
205
206
207
char *usagestr = 
 "usage: tmcd [-d] [-p #]\n"
 " -d              Turn on debugging. Multiple -d options increase output\n"
 " -p portnum	   Specify a port number to listen on\n"
208
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
209
210
211
212
213
 "\n";

void
usage()
{
214
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
215
216
217
	exit(1);
}

218
219
220
221
222
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
223
	killpg(0, SIGHUP);
224
225
}

Mike Hibler's avatar
Mike Hibler committed
226
227
int
main(int argc, char **argv)
228
{
229
230
231
232
	int			tcpsock, udpsock, i, ch, foo[4];
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
233
234
	FILE			*fp;
	char			buf[BUFSIZ];
235
	struct hostent		*he;
236
	extern char		build_info[];
237

238
	while ((ch = getopt(argc, argv, "dp:c:X")) != -1)
239
240
241
242
243
244
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
245
			break;
246
247
248
		case 'c':
			maxchildren = atoi(optarg);
			break;
249
250
251
252
253
#ifdef LBS
		case 'X':
			insecure = 1;
			break;
#endif
254
255
256
257
258
259
260
261
262
263
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
264
265
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
266

267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
	if (debug) 
		loginit(0, 0);
	else {
		/* Become a daemon */
		daemon(0, 0);
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
282

Austin Clements's avatar
Austin Clements committed
283
284
285
286
287
288
289
290
291
292
293
294
295
296
	/*
	 * Get FS's SFS hostid
	 * XXX This approach is somewhat kludgy
	 */
	strcpy(fshostid, "");
	if (access(FSHOSTID,R_OK) == 0) {
		fp = fopen(FSHOSTID, "r");
		if (!fp) {
			error("Failed to get FS's hostid");
		}
		else {
			fgets(fshostid, HOSTID_SIZE, fp);
			if (rindex(fshostid, '\n')) {
				*rindex(fshostid, '\n') = 0;
297
298
299
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
300
301
302
303
304
305
306
307
308
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
	
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
	/*
	 * Grab our IP for security check below.
	 */
#ifdef	LBS
	strcpy(buf, BOSSNODE);
#else
	if (gethostname(buf, sizeof(buf)) < 0)
		pfatal("getting hostname");
#endif
	if ((he = gethostbyname(buf)) == NULL) {
		error("Could not get IP (%s) - %s\n", buf, hstrerror(h_errno));
		exit(1);
	}
	memcpy((char *)&myipaddr, he->h_addr, he->h_length);

324
325
326
327
328
329
	/*
	 * If we were given a port on the command line, don't open the 
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
330
331
		error("Could not make sockets!");
		exit(1);
332
333
334
335
336
337
338
	    }
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
	}

	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);

	/*
	 * Stash the pid away.
	 */
	sprintf(buf, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(buf, "w");
	if (fp != NULL) {
		fprintf(fp, "%d\n", getpid());
		(void) fclose(fp);
	}

	/*
	 * Now fork a set of children to handle requests. We keep the
	 * pool at a set level. No need to get too fancy at this point,
	 * although this approach *is* rather bogus. 
	 */
	bzero(foo, sizeof(foo));
	while (1) {
		while (!killme && numchildren < maxchildren) {
			int which = 0;
			if (!foo[1])
				which = 1;
366
			else if (!debug && !foo[2])
367
				which = 2;
368
			else if (!debug && !foo[3])
369
				which = 3;
370

371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
				foo[which] = pid;
				numchildren++;
				continue;
			}
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
			
			switch (which) {
			case 0: tcpserver(tcpsock);
				break;
			case 1: udpserver(udpsock);
				break;
			case 2: udpserver(altudpsock);
				break;
			case 3: tcpserver(alttcpsock);
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
		}
406
407
408
409
410
		if( WIFSIGNALED(status) ) {
		  error("server %d exited with signal %d!\n", pid, WTERMSIG(status));
		} else if( WIFEXITED(status) ) {
		  error("server %d exited with status %d!\n", pid, WEXITSTATUS(status));	  
		}
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
		numchildren--;
		for (i = 0; i < (sizeof(foo)/sizeof(int)); i++) {
			if (foo[i] == pid)
				foo[i] = 0;
		}
		if (killme && !numchildren)
			break;
	}
 done:
	CLOSE(tcpsock);
	close(udpsock);
	info("daemon terminating\n");
	exit(0);
}

/*
 * Create sockets on specified port.
 */
static int
makesockets(int portnum, int *udpsockp, int *tcpsockp)
{
	struct sockaddr_in	name;
	int			length, i, udpsock, tcpsock;

Mike Hibler's avatar
Mike Hibler committed
435
	/*
436
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
437
438
	 */

439
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
440
441
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
442
		pfatal("opening stream socket");
443
444
	}

445
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
446
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
447
		       (char *)&i, sizeof(i)) < 0)
448
		pwarning("setsockopt(SO_REUSEADDR)");;
449
450
451
452
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
453
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
454
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
455
		pfatal("binding stream socket");
456
457
458
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
459
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
460
		pfatal("getsockname");
461
	}
462
	if (listen(tcpsock, 128) < 0) {
463
		pfatal("listen");
464
	}
465
466
	info("listening on TCP port %d\n", ntohs(name.sin_port));
	
Mike Hibler's avatar
Mike Hibler committed
467
468
469
470
471
472
473
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
474
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
475
476
477
478
479
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
480
		pwarning("setsockopt(SO_REUSEADDR)");;
Mike Hibler's avatar
Mike Hibler committed
481
482
483
484
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
485
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
486
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
487
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
488
489
490
491
492
	}

	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(udpsock, (struct sockaddr *) &name, &length)) {
493
		pfatal("getsockname");
Mike Hibler's avatar
Mike Hibler committed
494
	}
495
	info("listening on UDP port %d\n", ntohs(name.sin_port));
496

497
498
499
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
500
}
501

502
/*
503
 * Listen for UDP requests. This is not a secure channel, and so this should
504
505
506
507
508
509
510
511
512
 * eventually be killed off.
 */
static void
udpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc;
	
513
	info("udpserver starting: pid=%d sock=%d\n", getpid(), sock);
514
515
516
517
518
519

	/*
	 * Wait for udp connections.
	 */
	while (1) {
		length = sizeof(client);		
520
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
521
522
523
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
524
525
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
526
			continue;
527
		}
528
529
530
531
532
533
534
		buf[cc] = '\0';
		handle_request(sock, &client, buf, 0);
	}
	exit(1);
}

/*
535
 * Listen for TCP requests.
536
537
538
539
540
541
542
543
 */
static void
tcpserver(int sock)
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
	int			length, cc, newsock;
	
544
	info("tcpserver starting: pid=%d sock=%d\n", getpid(), sock);
545
546
547
548
549
550

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
		length  = sizeof(client);
551
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length);
552
		if (newsock < 0) {
553
			errorc("accepting TCP connection");
554
			continue;
555
		}
Mike Hibler's avatar
Mike Hibler committed
556
557

		/*
558
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
559
		 */
560
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
561
			if (cc < 0)
562
563
564
				errorc("Reading TCP request");
			error("TCP connection aborted\n");
			CLOSE(newsock);
565
			continue;
566
		}
567
568
		buf[cc] = '\0';
		handle_request(newsock, &client, buf, 1);
569
		CLOSE(newsock);
570
571
572
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
573

574
575
576
577
static int
handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
{
	struct sockaddr_in redirect_client;
578
	int		   redirect = 0, havekey = 0;
579
	char		   buf[BUFSIZ], *bp, *cp;
580
	char		   privkey[TBDB_FLEN_PRIVKEY];
581
	int		   i, err = 0;
582
	int		   version = DEFAULT_VERSION;
583
584
585
586
587
588
589
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;


	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
590

591
592
593
	/*
	 * Look for special tags. 
	 */
594
	bp = rdata;
595
	while ((bp = strsep(&rdata, " ")) != NULL) {
596
597
598
599
600
601
602
603
604
605
606
607
608
609
		/*
		 * Look for PRIVKEY. 
		 */
		if (sscanf(bp, "PRIVKEY=%64s", buf)) {
			havekey = 1;
			strncpy(privkey, buf, sizeof(privkey));

			if (debug) {
				info("PRIVKEY %s\n", buf);
			}
			continue;
		}


610
611
612
613
614
615
616
		/*
		 * Look for VERSION. 
		 */
		if (sscanf(bp, "VERSION=%d", &i) == 1) {
			version = i;
			continue;
		}
Mike Hibler's avatar
Mike Hibler committed
617

618
619
620
621
622
623
624
625
626
627
		/*
		 * Look for REDIRECT, which is a proxy request for a
		 * client other than the one making the request. Good
		 * for testing. Might become a general tmcd redirect at
		 * some point, so that we can test new tmcds.
		 */
		if (sscanf(bp, "REDIRECT=%30s", buf)) {
			redirect_client = *client;
			redirect        = 1;
			inet_aton(buf, &client->sin_addr);
628

629
630
			info("REDIRECTED from %s to %s\n",
			     inet_ntoa(redirect_client.sin_addr), buf);
631

632
633
634
635
636
637
638
639
640
641
642
			continue;
		}
		
		/*
		 * Look for VNODE. This is used for virtual nodes.
		 * It indicates which of the virtual nodes (on the physical
		 * node) is talking to us. Currently no perm checking.
		 * Very temporary approach; should be done via a per-vnode
		 * cert or a key.
		 */
		if (sscanf(bp, "VNODEID=%30s", buf)) {
643
644
			reqp->isvnode = 1;
			strncpy(reqp->vnodeid, buf, sizeof(reqp->vnodeid));
645

646
647
648
649
650
			if (debug) {
				info("VNODEID %s\n", buf);
			}
			continue;
		}
651

652
653
654
655
656
		/*
		 * An empty token (two delimiters next to each other)
		 * is indicated by a null string. If nothing matched,
		 * and its not an empty token, it must be the actual
		 * command and arguments. Break out.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
657
658
659
		 *
		 * Note that rdata will point to any text after the command.
		 *
660
661
662
663
664
		 */
		if (*bp) {
			break;
		}
	}
665

666
667
668
	/* Start with default DB */
	strcpy(dbname, DEFAULT_DBNAME);

669
	/*
670
	 * Map the ip to a nodeid.
671
	 */
672
	if ((err = iptonodeid(client->sin_addr, reqp))) {
673
674
		if (err == 2) {
			error("No such node vnode mapping %s on %s\n",
675
			      reqp->vnodeid, reqp->nodeid);
676
677
678
679
680
		}
		else {
			error("No such node: %s\n",
			      inet_ntoa(client->sin_addr));
		}
681
682
683
684
685
686
687
688
		goto skipit;
	}

	/*
	 * Redirect is allowed from the local host only!
	 * I use this for testing. See below where I test redirect
	 * if the verification fails. 
	 */
689
	if (!insecure && redirect &&
690
	    redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
691
692
693
694
695
		char	buf1[32], buf2[32];
		
		strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
		strcpy(buf2, inet_ntoa(client->sin_addr));
			
696
		info("%s INVALID REDIRECT: %s\n", buf1, buf2);
697
698
		goto skipit;
	}
699
700
701
702
703
704

#ifdef  WITHSSL
	/*
	 * If the connection is not SSL, then it must be a local node.
	 */
	if (isssl) {
705
706
707
		if (tmcd_sslverify_client(reqp->nodeid, reqp->class,
					  reqp->type, reqp->islocal)) {
			error("%s: SSL verification failure\n", reqp->nodeid);
708
709
710
711
			if (! redirect)
				goto skipit;
		}
	}
712
	else if (!reqp->islocal) {
713
714
715
		if (!istcp)
			goto execute;
		
716
		error("%s: Remote node connected without SSL!\n",reqp->nodeid);
717
718
		if (!insecure)
			goto skipit;
719
720
721
722
723
	}
#else
	/*
	 * When not compiled for ssl, do not allow remote connections.
	 */
724
	if (!reqp->islocal) {
725
726
		error("%s: Remote node connection not allowed (Define SSL)!\n",
		      reqp->nodeid);
727
728
		if (!insecure)
			goto skipit;
729
	}
730
731
732
733
#endif
	/*
	 * Check for a redirect using the default DB. This allows
	 * for a simple redirect to a secondary DB for testing.
734
	 * Upon return, the dbname has been changed if redirected.
735
	 */
736
	if (checkdbredirect(reqp)) {
737
738
739
		/* Something went wrong */
		goto skipit;
	}
740

741
742
743
744
745
	/*
	 * Do private key check. widearea nodes must report a private key
	 * It comes over ssl of course. At present we skip this check for
	 * ron nodes. 
	 */
746
	if (!reqp->islocal) {
747
		if (!havekey) {
748
			error("%s: No privkey sent!\n", reqp->nodeid);
749
750
751
752
753
754
755
			/*
			 * Skip. Okay, the problem is that the nodes out
			 * there are not reporting the key!
			goto skipit;
			 */
		}
		else if (checkprivkey(client->sin_addr, privkey)) {
756
757
			error("%s: privkey mismatch: %s!\n",
			      reqp->nodeid, privkey);
758
759
760
761
			goto skipit;
		}
	}

762
763
764
	/*
	 * Figure out what command was given.
	 */
765
 execute:
766
	for (i = 0; i < numcommands; i++)
767
		if (strncmp(bp, command_array[i].cmdname,
768
769
			    strlen(command_array[i].cmdname)) == 0)
			break;
Mike Hibler's avatar
Mike Hibler committed
770

771
	if (i == numcommands) {
772
		info("%s: INVALID REQUEST: %.8s\n", reqp->nodeid, bp);
773
774
		goto skipit;
	}
775

776
777
778
779
780
781
782
783
784
	/*
	 * XXX: We allow remote nodes to use UDP for isalive only!
	 */
	if (!istcp && !reqp->islocal && command_array[i].func != doisalive) {
		error("%s: Invalid request (%s) from remote node using UDP!\n",
		      reqp->nodeid, command_array[i].cmdname);
		goto skipit;
	}

785
786
787
	/*
	 * Execute it.
	 */
788
789
790
791
792
#ifdef	WITHSSL
	cp = isssl ? "ssl:yes" : "ssl:no";
#else
	cp = "";
#endif
793
794
795
796
797
	/*
	 * XXX hack, don't log "log" contents,
	 * both for privacy and to keep our syslog smaller.
	 */
	if (command_array[i].func == dolog)
798
		info("%s: vers:%d %s %s log %d chars\n", reqp->nodeid, version,
799
		     istcp ? "TCP" : "UDP", cp, strlen(rdata));
800
	else
801
		info("%s: vers:%d %s %s %s\n", reqp->nodeid, version,
802
		     istcp ? "TCP" : "UDP", cp, command_array[i].cmdname);
803

804
	err = command_array[i].func(sock, reqp, rdata, istcp, version);
805
806
807

	if (err)
		info("%s: %s: returned %d\n",
808
		     reqp->nodeid, command_array[i].cmdname, err);
809

810
 skipit:
811
812
813
	if (!istcp) 
		client_writeback_done(sock,
				      redirect ? &redirect_client : client);
814
	return 0;
815
816
817
818
819
}

/*
 * Accept notification of reboot. 
 */
820
COMMAND_PROTOTYPE(doreboot)
821
{
822
	/*
823
824
	 * This is now a no-op. The things this used to do are now
	 * done by stated when we hit RELOAD/RELOADDONE state
825
	 */
826
827
	return 0;
}
828

829
830
831
832
833
834
835
/*
 * Return emulab nodeid (not the experimental name).
 */
COMMAND_PROTOTYPE(donodeid)
{
	char		buf[MYBUFSIZE];

836
	sprintf(buf, "%s\n", reqp->nodeid);
837
	client_writeback(sock, buf, strlen(buf), tcp);
838
839
840
841
842
843
	return 0;
}

/*
 * Return status of node. Is it allocated to an experiment, or free.
 */
844
COMMAND_PROTOTYPE(dostatus)
845
{
Mike Hibler's avatar
Mike Hibler committed
846
	char		buf[MYBUFSIZE];
847
848
849
850

	/*
	 * Now check reserved table
	 */
851
852
	if (! reqp->allocated) {
		info("STATUS: %s: Node is free\n", reqp->nodeid);
853
854
855
		strcpy(buf, "FREE\n");
		client_writeback(sock, buf, strlen(buf), tcp);
		return 0;
856
857
	}

858
859
	sprintf(buf, "ALLOCATED=%s/%s NICKNAME=%s\n",
		reqp->pid, reqp->eid, reqp->nickname);
860
861
	client_writeback(sock, buf, strlen(buf), tcp);

862
	info("STATUS: %s: %s", reqp->nodeid, buf);
863
864
865
866
867
868
	return 0;
}

/*
 * Return ifconfig information to client.
 */
869
COMMAND_PROTOTYPE(doifconfig)
870
871
872
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
Mike Hibler's avatar
Mike Hibler committed
873
	char		buf[MYBUFSIZE];
874
875
876
877
878
	int		control_net, nrows;

	/*
	 * Now check reserved table
	 */
879
880
	if (! reqp->allocated) {
		info("IFCONFIG: %s: Node is free\n", reqp->nodeid);
881
882
883
884
885
886
887
		return 1;
	}

	/*
	 * Need to know the control network for the machine since
	 * we don't want to mess with that.
	 */
888
889
	if (nodeidtocontrolnet(reqp->nodeid, &control_net)) {
		error("IFCONFIG: %s: No Control Network\n", reqp->nodeid);
890
891
892
893
894
895
		return 1;
	}

	/*
	 * Find all the interfaces.
	 */
896
897
	res = mydb_query("select card,IP,IPalias,MAC,current_speed,duplex "
			 "from interfaces where node_id='%s'",
898
			 6, reqp->nodeid);
899
	if (!res) {
900
901
		error("IFCONFIG: %s: DB Error getting interfaces!\n",
		      reqp->nodeid);
902
903
904
905
		return 1;
	}

	if ((nrows = (int)mysql_num_rows(res)) == 0) {
906
		error("IFCONFIG: %s: No interfaces!\n", reqp->nodeid);
907
908
909
910
911
912
		mysql_free_result(res);
		return 1;
	}
	while (nrows) {
		row = mysql_fetch_row(res);
		if (row[1] && row[1][0]) {
913
914
915
916
			int card    = atoi(row[0]);
			char *speed  = "100";
			char *unit   = "Mbps";
			char *duplex = "full";
917

918
919
920
921
922
923
			/*
			 * INTERFACE can go away when all machines running
			 * updated (MAC based) setup. Right now MAC is at
			 * the end (see below) cause of the sharks, but they
			 * should be dead soon too.
			 */
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
			sprintf(buf, "INTERFACE=%d INET=%s MASK=%s",
				card, row[1], NETMASK);

			/*
			 * The point of this sillyness is to look for the
			 * special Shark case. The sharks have only one
			 * interface, so we use an IPalias on what we call
			 * the "control" interface. This test prevents us
			 * from returning an ifconfig line for the control
			 * interface, unless it has an IP alias. I could
			 * change the setup scripts to ignore this case on
			 * the PCs. Might end up doing that at some point. 
			 */
			if (row[2] && row[2][0]) {
				strcat(buf, " IPALIAS=");
				strcat(buf, row[2]);
			}
			else if (card == control_net)
				goto skipit;

944
945
946
947
948
949
950
			/*
			 * Tack on MAC, which should go up above after
			 * Sharks are sunk.
			 */
			strcat(buf, " MAC=");
			strcat(buf, row[3]);

951
952
953
954
955
956
957
958
959
960
961
962
963
			/*
			 * Tack on speed and duplex. 
			 */
			if (row[4] && row[4][0]) {
				speed = row[4];
			}
			if (row[5] && row[5][0]) {
				duplex = row[5];
			}
			
			sprintf(&buf[strlen(buf)],
				" SPEED=%s%s DUPLEX=%s", speed, unit, duplex);

964
			strcat(buf, "\n");
Mike Hibler's avatar
Mike Hibler committed
965
			client_writeback(sock, buf, strlen(buf), tcp);
966
			info("IFCONFIG: %s", buf);
967
		}
968
	skipit:
969
970
971
972
973
974
975
976
977
978
		nrows--;
	}
	mysql_free_result(res);

	return 0;
}

/*
 * Return account stuff.
 */
979
COMMAND_PROTOTYPE(doaccounts)
980
981
982
{
	MYSQL_RES	*res;	
	MYSQL_ROW	row;
Mike Hibler's avatar
Mike Hibler committed
983
	char		buf[MYBUFSIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
984
	int		nrows, gidint;
985
	int		tbadmin, didwidearea = 0;
986

987
988
	if (! tcp) {
		error("ACCOUNTS: %s: Cannot give account info out over UDP!\n",
989
		      reqp->nodeid);
990
991
992
993
994
995
		return 1;
	}

	/*
	 * Now check reserved table
	 */
996
997
	if ((reqp->islocal || reqp->isvnode) && !reqp->allocated) {
		error("ACCOUNTS: %s: Node is free\n", reqp->nodeid);
998
999
1000
		return 1;
	}