tmcd.c 334 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1
/*
2
 * Copyright (c) 2000-2015 University of Utah and the Flux Group.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 
 * {{{EMULAB-LICENSE
 * 
 * This file is part of the Emulab network testbed software.
 * 
 * This file is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This file is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 * License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this file.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * }}}
Leigh B. Stoller's avatar
Leigh B. Stoller committed
22 23
 */

24 25 26
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
Mike Hibler's avatar
Mike Hibler committed
27
#include <arpa/inet.h>
28
#include <netdb.h>
Mike Hibler's avatar
Mike Hibler committed
29
#include <ctype.h>
30
#include <stdio.h>
Mike Hibler's avatar
Mike Hibler committed
31
#include <stdlib.h>
32
#include <errno.h>
Mike Hibler's avatar
Mike Hibler committed
33 34
#include <string.h>
#include <unistd.h>
35 36 37 38
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <assert.h>
39
#include <sys/wait.h>
40
#include <sys/fcntl.h>
41 42
#include <sys/syscall.h>
#include <sys/stat.h>
43
#include <sys/param.h>
44
#include <paths.h>
Austin Clements's avatar
Austin Clements committed
45
#include <setjmp.h>
46 47
#include <pwd.h>
#include <grp.h>
48
#include <mysql/mysql.h>
49
#include "tmcd.h"
50
#include "config.h"
51 52
#include "ssl.h"
#include "log.h"
53
#include "tbdefs.h"
54
#include "bsdefs.h"
Leigh B. Stoller's avatar
Leigh B. Stoller committed
55 56
#include "bootwhat.h"
#include "bootinfo.h"
57

58 59 60 61
#ifdef EVENTSYS
#include "event.h"
#endif

62 63 64
/* XXX: Not sure this is okay! */
#include "tpm.h"

65 66 67
/*
 * XXX This needs to be localized!
 */
68 69 70
#define FSPROJDIR	FSNODE ":" FSDIR_PROJ
#define FSGROUPDIR	FSNODE ":" FSDIR_GROUPS
#define FSUSERDIR	FSNODE ":" FSDIR_USERS
71 72 73
#ifdef  FSDIR_SHARE
#define FSSHAREDIR	FSNODE ":" FSDIR_SHARE
#endif
74 75 76
#ifdef  FSDIR_SCRATCH
#define FSSCRATCHDIR	FSNODE ":" FSDIR_SCRATCH
#endif
77 78
/* XXX InstaGeni Rack Hack. Hack until I decide on a better way */
#define FSJAILIP       "172.17.253.254"
79 80 81 82 83
#define PROJDIR		PROJROOT_DIR
#define GROUPDIR	GROUPSROOT_DIR
#define USERDIR		USERSROOT_DIR
#define SCRATCHDIR	SCRATCHROOT_DIR
#define SHAREDIR	SHAREROOT_DIR
84
#define NETBEDDIR	"/netbed"
85
#define PLISALIVELOGDIR "/usr/testbed/log/plabisalive"
86 87
#define RELOADPID	"emulab-ops"
#define RELOADEID	"reloading"
88
#define FSHOSTID	"/usr/testbed/etc/fshostid"
89
#define DOTSFS		".sfs"
90 91
#define RUNASUSER	"nobody"
#define RUNASGROUP	"nobody"
Mike Hibler's avatar
Mike Hibler committed
92
#ifndef NTPSERVER
93
#define NTPSERVER       "ntp1"
Mike Hibler's avatar
Mike Hibler committed
94
#endif
95
#define PROTOUSER	"elabman"
96 97 98 99
#define PRIVKEY_LEN	128
#define URN_LEN		128
#define XSTRINGIFY(s)   STRINGIFY(s)
#define STRINGIFY(s)	#s
100

101 102 103 104 105
/* XXX backward compat */
#ifndef TBCOREDIR
#define	TBCOREDIR	TBROOT "/tmp"
#endif

106 107 108 109
/* socket read/write timeouts in ms */
#define READTIMO	3000
#define WRITETIMO	3000

110
#define TESTMODE
111 112 113 114 115 116 117 118 119 120 121 122
#define DEFAULTNETMASK	"255.255.255.0"
/* This can be tossed once all the changes are in place */
static char *
CHECKMASK(char *arg)
{
	if (arg && arg[0])
		return arg;

	error("No netmask defined!\n");
	return DEFAULTNETMASK;
}
/* #define CHECKMASK(arg)  ((arg) && (arg[0]) ? (arg) : DEFAULTNETMASK) */
123

124 125 126
#define DISKTYPE	"ad"
#define DISKNUM		0

127 128
/* Compiled in slothd parameters
 *
129
 * 1 - reg_interval  2 - agg_interval  3 - load_thresh
130 131 132 133
 * 4 - expt_thresh   5 - ctl_thresh
 */
#define SDPARAMS        "reg=300 agg=5 load=1 expt=5 ctl=1000"

134 135 136 137 138 139 140 141 142 143
/*
 * Compiled in tarball/rpm param.
 * Should come out of the DB.
 */
#ifdef SPEWFROMOPS
#define TARRPM_SERVER	USERNODE
#else
#define TARRPM_SERVER	BOSSNODE
#endif

144 145 146 147 148 149
#ifdef IMAGEPROVENANCE
#define WITHPROVENANCE	1
#else
#define WITHPROVENANCE	0
#endif

150 151 152 153 154 155 156
#ifdef WITHZFS
#undef WITHZFS
#define WITHZFS	1
#else
#define WITHZFS	0
#endif

157 158
/* Defined in configure and passed in via the makefile */
#define DBNAME_SIZE	64
Austin Clements's avatar
Austin Clements committed
159
#define HOSTID_SIZE	(32+64)
160 161
#define DEFAULT_DBNAME	TBDBNAME

Robert P Ricci's avatar
Robert P Ricci committed
162 163 164 165
/* For secure disk loading */
#define SECURELOAD_OPMODE "SECURELOAD"
#define SECURELOAD_STATE  "RELOADSETUP"

166 167 168
/* Our answer to "geni-get --version" */
#define GENI_VERSION "1"

169 170 171 172 173 174 175 176
/* Taint support */
#define TB_TAINTSTATE_BLACKBOX  1
#define TB_TAINTSTATE_USERONLY  2
#define TB_TAINTSTATE_DANGEROUS 4
#define HAS_ANY_TAINTS(tset, tcheck) (tset & tcheck)
#define HAS_ALL_TAINTS(tset, tcheck) ((tset & tcheck) == tcheck)
#define HAS_TAINT(tset, tcheck) HAS_ALL_TAINTS(tset, tcheck)

177 178 179 180 181 182 183
typedef struct {
	char pid[TBDB_FLEN_PID];
	char gid[TBDB_FLEN_GID];
	char name[TBDB_FLEN_IMAGENAME];
	char version[11]; /* Max size of a 32-bit int in string form. */
} imstrings_t;

184
int		debug = 0;
185
static int	verbose = 0;
186
static int	insecure = 0;
187
static int	byteswritten = 0;
188
static char	pidfile[MAXPATHLEN];
189
static char     dbname[DBNAME_SIZE];
190
static struct in_addr myipaddr;
191
static struct in_addr cnet, cmask, jnet, jmask;
Austin Clements's avatar
Austin Clements committed
192
static char	fshostid[HOSTID_SIZE];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
193
static int	nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
194 195
static void	tcpserver(int sock, int portnum);
static void	udpserver(int sock, int portnum);
196
static int      handle_request(int, struct sockaddr_in *, char *, int, int);
197
static int      checkcerts(char*);
198
static int	makesockets(int portnum, int *udpsockp, int *tcpsockp);
Mike Hibler's avatar
Mike Hibler committed
199 200
int		client_writeback(int sock, void *buf, int len, int tcp);
void		client_writeback_done(int sock, struct sockaddr_in *client);
201 202
MYSQL_RES *	mydb_query(char *query, int ncols, ...);
int		mydb_update(char *query, ...);
203
static int	safesymlink(char *name1, char *name2);
204
static int      getImageInfo(char *path, char *nodeid, char *pid, char *imagename,
205
			     unsigned int *mtime, off_t *isize);
206
static int	getrandomchars(char *buf, int len);
207

208 209 210 211
/* socket timeouts */
static int	readtimo = READTIMO;
static int	writetimo = WRITETIMO;

212
/* thread support */
213
#define MAXCHILDREN	20
214
#define MINCHILDREN	8
215
static int	numchildren;
216 217 218 219
static int	maxchildren       = 13;
static int      num_udpservers    = 3;
static int      num_altudpservers = 1;
static int      num_alttcpservers = 1;
220
static int	mypid;
221
static volatile int killme;
222

223 224 225 226 227
/* Output macro to check for string overflow */
#define OUTPUT(buf, size, format...) \
({ \
	int __count__ = snprintf((buf), (size), ##format); \
        \
228
        if (__count__ >= (size)) { \
229 230 231 232 233 234
		error("Not enough room in output buffer! line %d.\n", __LINE__);\
		return 1; \
	} \
	__count__; \
})

235 236 237 238 239
/*
 * 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 {
Leigh B. Stoller's avatar
Leigh B. Stoller committed
240
	struct in_addr  client;
241 242 243
	int		allocated;
	int		jailflag;
	int		isvnode;
244
	int		asvnode;
245
	int		issubnode;
246
	int		islocal;
247
	int		isdedicatedwa;
248
	int		iscontrol;
249
	int		isplabdslice;
250
	int		isplabsvc;
251
	int		elab_in_elab;
252
        int		singlenet;	  /* Modifier for elab_in_elab */
253
	int		update_accounts;
254
	int		exptidx;
255 256 257
	int		creator_idx;
	int		swapper_idx;
	int		swapper_isadmin;
258
        int		genisliver_idx;
259
        int		geniflags;
260
        int		nonfsmounts;
261
	unsigned int    taintstates;
262 263
	char		nodeid[TBDB_FLEN_NODEID];
	char		vnodeid[TBDB_FLEN_NODEID];
Leigh B. Stoller's avatar
Leigh B. Stoller committed
264
	char		pnodeid[TBDB_FLEN_NODEID]; /* XXX */
265 266 267 268 269
	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];
270
	char		class[TBDB_FLEN_NODECLASS];
271 272
        char		ptype[TBDB_FLEN_NODETYPE];	/* Of physnode */
	char		pclass[TBDB_FLEN_NODECLASS];	/* Of physnode */
273 274
	char		creator[TBDB_FLEN_UID];
	char		swapper[TBDB_FLEN_UID];
275
	char		syncserver[TBDB_FLEN_VNAME];	/* The vname */
276
	char		keyhash[TBDB_FLEN_PRIVKEY];
277
	char		eventkey[TBDB_FLEN_PRIVKEY];
278
	char		sfshostid[TBDB_FLEN_SFSHOSTID];
279
	char		testdb[TBDB_FLEN_TINYTEXT];
280
	char		sharing_mode[TBDB_FLEN_TINYTEXT];
281
	char		erole[TBDB_FLEN_TINYTEXT];
282
	char            privkey[PRIVKEY_LEN+1];
283
	char		nodeuuid[TBDB_FLEN_UUID];
284 285
        /* This key is a replacement for privkey, on protogeni resources */
	char            external_key[PRIVKEY_LEN+1];
286
} tmcdreq_t;
287
static int	iptonodeid(struct in_addr, tmcdreq_t *, char*);
288
static int	checkdbredirect(tmcdreq_t *);
289
static int      sendstoreconf(int sock, int tcp, tmcdreq_t *reqp, char *bscmd, 
290
			      char *vname, int dopersist);
291
static int      get_imagestrings(tmcdreq_t *reqp, imstrings_t *imstrings);
292

293 294 295 296 297
#ifdef EVENTSYS
int			myevent_send(address_tuple_t address);
static event_handle_t	event_handle = NULL;
#endif

298 299 300
/*
 * Commands we support.
 */
301 302
#define COMMAND_PROTOTYPE(x) \
	static int \
303
	x(int sock, tmcdreq_t *reqp, char *rdata, int tcp, int vers)
304 305

COMMAND_PROTOTYPE(doreboot);
306
COMMAND_PROTOTYPE(donodeid);
307
COMMAND_PROTOTYPE(donodeuuid);
David Johnson's avatar
David Johnson committed
308
COMMAND_PROTOTYPE(domanifest);
309 310 311
COMMAND_PROTOTYPE(dostatus);
COMMAND_PROTOTYPE(doifconfig);
COMMAND_PROTOTYPE(doaccounts);
312
COMMAND_PROTOTYPE(dobridges);
313
COMMAND_PROTOTYPE(dodelay);
314
COMMAND_PROTOTYPE(dolinkdelay);
315 316 317 318
COMMAND_PROTOTYPE(dohosts);
COMMAND_PROTOTYPE(dorpms);
COMMAND_PROTOTYPE(dodeltas);
COMMAND_PROTOTYPE(dotarballs);
319
COMMAND_PROTOTYPE(doblobs);
320 321 322 323
COMMAND_PROTOTYPE(dostartcmd);
COMMAND_PROTOTYPE(dostartstat);
COMMAND_PROTOTYPE(doready);
COMMAND_PROTOTYPE(doreadycount);
324
COMMAND_PROTOTYPE(dostorageconfig);
325
COMMAND_PROTOTYPE(domounts);
Austin Clements's avatar
Austin Clements committed
326
COMMAND_PROTOTYPE(dosfshostid);
327 328 329 330 331 332 333
COMMAND_PROTOTYPE(doloadinfo);
COMMAND_PROTOTYPE(doreset);
COMMAND_PROTOTYPE(dorouting);
COMMAND_PROTOTYPE(dotrafgens);
COMMAND_PROTOTYPE(donseconfigs);
COMMAND_PROTOTYPE(dostate);
COMMAND_PROTOTYPE(docreator);
334
COMMAND_PROTOTYPE(dotunnels);
335
COMMAND_PROTOTYPE(dovnodelist);
336
COMMAND_PROTOTYPE(dosubnodelist);
337
COMMAND_PROTOTYPE(doisalive);
338
COMMAND_PROTOTYPE(doipodinfo);
339 340
COMMAND_PROTOTYPE(dontpinfo);
COMMAND_PROTOTYPE(dontpdrift);
341
COMMAND_PROTOTYPE(dojailconfig);
342
COMMAND_PROTOTYPE(doplabconfig);
343 344
COMMAND_PROTOTYPE(dosubconfig);
COMMAND_PROTOTYPE(doixpconfig);
345
COMMAND_PROTOTYPE(doslothdparams);
346
COMMAND_PROTOTYPE(doprogagents);
347
COMMAND_PROTOTYPE(dosyncserver);
348
COMMAND_PROTOTYPE(dokeyhash);
349
COMMAND_PROTOTYPE(doeventkey);
350
COMMAND_PROTOTYPE(dofullconfig);
351 352
COMMAND_PROTOTYPE(doroutelist);
COMMAND_PROTOTYPE(dorole);
353
COMMAND_PROTOTYPE(dorusage);
354
COMMAND_PROTOTYPE(dodoginfo);
355
COMMAND_PROTOTYPE(dohostkeys);
Mike Hibler's avatar
Mike Hibler committed
356
COMMAND_PROTOTYPE(dotmcctest);
357
COMMAND_PROTOTYPE(dofwinfo);
358
COMMAND_PROTOTYPE(dohostinfo);
359
COMMAND_PROTOTYPE(doemulabconfig);
360
COMMAND_PROTOTYPE(doeplabconfig);
361
COMMAND_PROTOTYPE(dolocalize);
362
COMMAND_PROTOTYPE(dorootpswd);
363 364
COMMAND_PROTOTYPE(dobooterrno);
COMMAND_PROTOTYPE(dobootlog);
365
COMMAND_PROTOTYPE(dobattery);
366
COMMAND_PROTOTYPE(dotopomap);
367
COMMAND_PROTOTYPE(douserenv);
368 369
COMMAND_PROTOTYPE(dotiptunnels);
COMMAND_PROTOTYPE(dorelayconfig);
370
COMMAND_PROTOTYPE(dotraceconfig);
371
COMMAND_PROTOTYPE(doltmap);
372
COMMAND_PROTOTYPE(doltpmap);
373
COMMAND_PROTOTYPE(doelvindport);
374
COMMAND_PROTOTYPE(doplabeventkeys);
375
COMMAND_PROTOTYPE(dointfcmap);
376
COMMAND_PROTOTYPE(domotelog);
377
COMMAND_PROTOTYPE(doportregister);
Leigh B. Stoller's avatar
Leigh B. Stoller committed
378
COMMAND_PROTOTYPE(dobootwhat);
379 380
COMMAND_PROTOTYPE(dotpmblob);
COMMAND_PROTOTYPE(dotpmpubkey);
381
COMMAND_PROTOTYPE(dotpmdummy);
382
COMMAND_PROTOTYPE(dodhcpdconf);
383
COMMAND_PROTOTYPE(dosecurestate);
384
COMMAND_PROTOTYPE(doquoteprep);
385
COMMAND_PROTOTYPE(doimagekey);
386
COMMAND_PROTOTYPE(donodeattributes);
387
COMMAND_PROTOTYPE(dodisks);
388
COMMAND_PROTOTYPE(doarpinfo);
389
COMMAND_PROTOTYPE(dohwinfo);
390
COMMAND_PROTOTYPE(dotiplineinfo);
391
COMMAND_PROTOTYPE(doimageid);
392
COMMAND_PROTOTYPE(doimagesize);
393 394 395 396 397 398 399 400 401 402 403 404
#if PROTOGENI_SUPPORT
COMMAND_PROTOTYPE(dogeniclientid);
COMMAND_PROTOTYPE(dogenisliceurn);
COMMAND_PROTOTYPE(dogenisliceemail);
COMMAND_PROTOTYPE(dogeniuserurn);
COMMAND_PROTOTYPE(dogeniuseremail);
COMMAND_PROTOTYPE(dogenigeniuser);
COMMAND_PROTOTYPE(dogenimanifest);
COMMAND_PROTOTYPE(dogenicontrolmac);
COMMAND_PROTOTYPE(dogeniversion);
COMMAND_PROTOTYPE(dogenigetversion);
COMMAND_PROTOTYPE(dogenisliverstatus);
405 406
COMMAND_PROTOTYPE(dogenistatus);
COMMAND_PROTOTYPE(dogenicommands);
407
COMMAND_PROTOTYPE(dogeniall);
408
COMMAND_PROTOTYPE(dogeniinvalid);
409
#endif
410

411 412
/*
 * The fullconfig slot determines what routines get called when pushing
413
 * out a full configuration. Physnodes get slightly different
414
 * than vnodes, and at some point we might want to distinguish different
415 416 417 418 419
 * types of vnodes (jailed, plab).
 */
#define FULLCONFIG_NONE		0x0
#define FULLCONFIG_PHYS		0x1
#define FULLCONFIG_VIRT		0x2
420 421 422 423 424
#define FULLCONFIG_ALL		(FULLCONFIG_PHYS|FULLCONFIG_VIRT)

/*
 * Flags encode a few other random properties of commands
 */
425 426 427 428 429
#define F_REMUDP	0x01	/* remote nodes can request using UDP */
#define F_MINLOG	0x02	/* record minimal logging info normally */
#define F_MAXLOG	0x04	/* record maximal logging info normally */
#define F_ALLOCATED	0x08	/* node must be allocated to make call */
#define F_REMNOSSL	0x10	/* remote nodes can request without SSL */
430
#define F_REMREQSSL	0x20	/* remote nodes must connect with SSL */
431
#define F_REQTPM	0x40	/* require TPM on client */
432

433 434
struct command {
	char	*cmdname;
435
	int	fullconfig;
436
	int	flags;
437
	int    (*func)(int, tmcdreq_t *, char *, int, int);
438
} command_array[] = {
439 440
	{ "reboot",	  FULLCONFIG_NONE, 0, doreboot },
	{ "nodeid",	  FULLCONFIG_ALL,  0, donodeid },
441
	{ "nodeuuid",	  FULLCONFIG_ALL,  0, donodeuuid },
David Johnson's avatar
David Johnson committed
442
	{ "manifest",	  FULLCONFIG_ALL,  0, domanifest },
443
	{ "status",	  FULLCONFIG_NONE, 0, dostatus },
444
	{ "ifconfig",	  FULLCONFIG_ALL,  F_ALLOCATED, doifconfig },
445
	{ "accounts",	  FULLCONFIG_ALL,  F_REMREQSSL, doaccounts },
446
	{ "delay",	  FULLCONFIG_ALL,  F_ALLOCATED, dodelay },
447
	{ "bridges",	  FULLCONFIG_ALL,  F_ALLOCATED, dobridges },
448 449 450 451 452
	{ "linkdelay",	  FULLCONFIG_ALL,  F_ALLOCATED, dolinkdelay },
	{ "hostnames",	  FULLCONFIG_NONE, F_ALLOCATED, dohosts },
	{ "rpms",	  FULLCONFIG_ALL,  F_ALLOCATED, dorpms },
	{ "deltas",	  FULLCONFIG_NONE, F_ALLOCATED, dodeltas },
	{ "tarballs",	  FULLCONFIG_ALL,  F_ALLOCATED, dotarballs },
453
	{ "blobs",	  FULLCONFIG_ALL,  F_ALLOCATED, doblobs },
454 455
	{ "startupcmd",	  FULLCONFIG_ALL,  F_ALLOCATED, dostartcmd },
	{ "startstatus",  FULLCONFIG_NONE, F_ALLOCATED, dostartstat }, /* Before startstat*/
456
	{ "startstat",	  FULLCONFIG_NONE, 0, dostartstat },
457 458
	{ "readycount",   FULLCONFIG_NONE, F_ALLOCATED, doreadycount },
	{ "ready",	  FULLCONFIG_NONE, F_ALLOCATED, doready },
459
	{ "storageconfig", FULLCONFIG_ALL, F_ALLOCATED, dostorageconfig},
460 461
	{ "mounts",	  FULLCONFIG_ALL,  F_ALLOCATED, domounts },
	{ "sfshostid",	  FULLCONFIG_NONE, F_ALLOCATED, dosfshostid },
462 463
	{ "loadinfo",	  FULLCONFIG_NONE, 0, doloadinfo},
	{ "reset",	  FULLCONFIG_NONE, 0, doreset},
464 465 466 467
	{ "routing",	  FULLCONFIG_ALL,  F_ALLOCATED, dorouting},
	{ "trafgens",	  FULLCONFIG_ALL,  F_ALLOCATED, dotrafgens},
	{ "nseconfigs",	  FULLCONFIG_ALL,  F_ALLOCATED, donseconfigs},
	{ "creator",	  FULLCONFIG_ALL,  F_ALLOCATED, docreator},
468
	{ "state",	  FULLCONFIG_NONE, 0, dostate},
469
	{ "tunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotunnels},
470
	{ "vnodelist",	  FULLCONFIG_PHYS, 0, dovnodelist},
471
	{ "subnodelist",  FULLCONFIG_PHYS, 0, dosubnodelist},
472
	{ "isalive",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, doisalive},
473
	{ "ipodinfo",	  FULLCONFIG_PHYS, 0, doipodinfo},
474 475
	{ "ntpinfo",	  FULLCONFIG_PHYS, 0, dontpinfo},
	{ "ntpdrift",	  FULLCONFIG_NONE, 0, dontpdrift},
476 477
	{ "jailconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, dojailconfig},
	{ "plabconfig",	  FULLCONFIG_VIRT, F_ALLOCATED, doplabconfig},
478
	{ "subconfig",	  FULLCONFIG_NONE, 0, dosubconfig},
479
        { "sdparams",     FULLCONFIG_PHYS, 0, doslothdparams},
480 481
        { "programs",     FULLCONFIG_ALL,  F_ALLOCATED, doprogagents},
        { "syncserver",   FULLCONFIG_ALL,  F_ALLOCATED, dosyncserver},
482
        { "keyhash",      FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, dokeyhash},
483
        { "eventkey",     FULLCONFIG_ALL,  F_ALLOCATED|F_REMREQSSL, doeventkey},
484 485 486
        { "fullconfig",   FULLCONFIG_NONE, F_ALLOCATED, dofullconfig},
        { "routelist",	  FULLCONFIG_PHYS, F_ALLOCATED, doroutelist},
        { "role",	  FULLCONFIG_PHYS, F_ALLOCATED, dorole},
487
        { "rusage",	  FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dorusage},
488
        { "watchdoginfo", FULLCONFIG_ALL,  F_REMUDP|F_MINLOG, dodoginfo},
489
        { "hostkeys",     FULLCONFIG_NONE, 0, dohostkeys},
Mike Hibler's avatar
Mike Hibler committed
490
        { "tmcctest",     FULLCONFIG_NONE, F_MINLOG, dotmcctest},
491
        { "firewallinfo", FULLCONFIG_ALL,  0, dofwinfo},
492
        { "hostinfo",     FULLCONFIG_NONE, 0, dohostinfo},
493
	{ "emulabconfig", FULLCONFIG_NONE, F_ALLOCATED, doemulabconfig},
494
	{ "eplabconfig",  FULLCONFIG_NONE, F_ALLOCATED, doeplabconfig},
495
	{ "localization", FULLCONFIG_PHYS, 0, dolocalize},
496
	{ "rootpswd",     FULLCONFIG_NONE, F_REMREQSSL, dorootpswd},
497 498
	{ "booterrno",    FULLCONFIG_NONE, 0, dobooterrno},
	{ "bootlog",      FULLCONFIG_NONE, 0, dobootlog},
499
	{ "battery",      FULLCONFIG_NONE, F_REMUDP|F_MINLOG, dobattery},
500
	{ "topomap",      FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, dotopomap},
501
	{ "userenv",      FULLCONFIG_ALL,  F_ALLOCATED, douserenv},
502
	{ "tiptunnels",	  FULLCONFIG_ALL,  F_ALLOCATED, dotiptunnels},
503
	{ "traceinfo",	  FULLCONFIG_ALL,  F_ALLOCATED, dotraceconfig },
504
	{ "ltmap",        FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltmap},
505
	{ "ltpmap",       FULLCONFIG_NONE, F_MINLOG|F_ALLOCATED, doltpmap},
506
	{ "elvindport",   FULLCONFIG_NONE, 0, doelvindport},
507
	{ "plabeventkeys",FULLCONFIG_NONE, F_REMREQSSL, doplabeventkeys},
508
	{ "intfcmap",     FULLCONFIG_NONE, 0, dointfcmap},
509 510
	{ "motelog",      FULLCONFIG_ALL,  F_ALLOCATED, domotelog},
	{ "portregister", FULLCONFIG_NONE, F_REMNOSSL, doportregister},
Leigh B. Stoller's avatar
Leigh B. Stoller committed
511
	{ "bootwhat",	  FULLCONFIG_NONE, 0, dobootwhat },
512 513
	{ "tpmblob",	  FULLCONFIG_ALL, 0, dotpmblob },
	{ "tpmpubkey",	  FULLCONFIG_ALL, 0, dotpmpubkey },
514
	{ "tpmdummy",	  FULLCONFIG_ALL, F_REQTPM, dotpmdummy },
515
	{ "dhcpdconf",	  FULLCONFIG_ALL, 0, dodhcpdconf },
516 517
	{ "securestate",  FULLCONFIG_NONE, F_REMREQSSL, dosecurestate},
	{ "quoteprep",    FULLCONFIG_NONE, F_REMREQSSL, doquoteprep},
518
	{ "imagekey",     FULLCONFIG_NONE, F_REQTPM, doimagekey},
519
	{ "nodeattributes", FULLCONFIG_ALL, 0, donodeattributes},
520
	{ "disks",	  FULLCONFIG_ALL, 0, dodisks},
521
	{ "arpinfo",	  FULLCONFIG_NONE, 0, doarpinfo},
522
	{ "hwinfo",	  FULLCONFIG_NONE, 0, dohwinfo},
523
	{ "tiplineinfo",  FULLCONFIG_NONE,  F_ALLOCATED, dotiplineinfo},
524
	{ "imageinfo",      FULLCONFIG_NONE,  F_ALLOCATED, doimageid},
525
	{ "imagesize",   FULLCONFIG_NONE,  F_ALLOCATED, doimagesize},
526 527 528 529 530 531 532 533 534 535 536 537 538
#if PROTOGENI_SUPPORT
	{ "geni_client_id", FULLCONFIG_NONE, 0, dogeniclientid },
	{ "geni_slice_urn", FULLCONFIG_NONE, 0, dogenisliceurn },
	{ "geni_slice_email", FULLCONFIG_NONE, 0, dogenisliceemail },
	{ "geni_user_urn", FULLCONFIG_NONE, 0, dogeniuserurn },
	{ "geni_user_email", FULLCONFIG_NONE, 0, dogeniuseremail },
	/* Yes, "geni_user" is a stupid name.  Wasn't my idea. */
	{ "geni_geni_user", FULLCONFIG_NONE, 0, dogenigeniuser },
	{ "geni_manifest", FULLCONFIG_NONE, 0, dogenimanifest },
	{ "geni_control_mac", FULLCONFIG_NONE, 0, dogenicontrolmac },
	{ "geni_version", FULLCONFIG_NONE, 0, dogeniversion },
	{ "geni_getversion", FULLCONFIG_NONE, 0, dogenigetversion },
	{ "geni_sliverstatus", FULLCONFIG_NONE, 0, dogenisliverstatus },
539 540
	{ "geni_status", FULLCONFIG_NONE, 0, dogenistatus },
	{ "geni_commands", FULLCONFIG_NONE, 0, dogenicommands },
541
	{ "geni_all",     FULLCONFIG_NONE, 0, dogeniall },
542 543 544
	/* A rather ugly hack to avoid making error handling a special case.
	   THIS MUST BE THE LAST ENTRY IN THE ARRAY! */
	{ "geni_invalid", FULLCONFIG_NONE, 0, dogeniinvalid }
545
#endif
546 547 548
};
static int numcommands = sizeof(command_array)/sizeof(struct command);

549
char *usagestr =
550 551 552
 "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"
553
 " -c num	   Specify number of servers (must be %d <= x <= %d)\n"
554
 " -v              More verbose logging\n"
555
 " -i ipaddr       Sets the boss IP addr to return (for multi-homed servers)\n"
556 557 558 559 560
 "\n";

void
usage()
{
561
	fprintf(stderr, usagestr, MINCHILDREN, MAXCHILDREN);
562 563 564
	exit(1);
}

565 566 567 568 569
static void
cleanup()
{
	signal(SIGHUP, SIG_IGN);
	killme = 1;
570
	killpg(0, SIGHUP);
571
	unlink(pidfile);
572 573
}

574 575 576 577
static void
setverbose(int sig)
{
	signal(sig, SIG_IGN);
578

579 580 581 582
	if (sig == SIGUSR1)
		verbose = 1;
	else
		verbose = 0;
583 584
	info("verbose logging turned %s\n", verbose ? "on" : "off");

585 586 587 588 589 590
	/* Just the parent sends this */
	if (numchildren)
		killpg(0, sig);
	signal(sig, setverbose);
}

Mike Hibler's avatar
Mike Hibler committed
591 592
int
main(int argc, char **argv)
593
{
594
	int			tcpsock, udpsock, i, ch;
595 596 597
	int			alttcpsock, altudpsock;
	int			status, pid;
	int			portnum = TBSERVER_PORT;
598 599
	FILE			*fp;
	char			buf[BUFSIZ];
600
	struct hostent		*he;
601
	extern char		build_info[];
602 603 604 605 606
	int			server_counts[4]; /* udp,tcp,altudp,alttcp */
	struct {
		int	pid;
		int	which;
	} servers[MAXCHILDREN];
607

608
	while ((ch = getopt(argc, argv, "dp:c:Xvi:")) != -1)
609 610 611 612 613 614
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
615
			break;
616 617 618
		case 'c':
			maxchildren = atoi(optarg);
			break;
619 620 621
		case 'X':
			insecure = 1;
			break;
622 623 624
		case 'v':
			verbose++;
			break;
625 626 627 628 629 630 631
		case 'i':
			if (inet_aton(optarg, &myipaddr) == 0) {
				fprintf(stderr, "invalid IP address %s\n",
					optarg);
				usage();
			}
			break;
632 633 634 635 636 637 638 639 640 641
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();
642 643
	if (maxchildren < MINCHILDREN || maxchildren > MAXCHILDREN)
		usage();
644

645 646 647 648 649 650
#ifdef  WITHSSL
	if (tmcd_server_sslinit()) {
		error("SSL init failed!\n");
		exit(1);
	}
#endif
651
	if (debug)
652 653 654
		loginit(0, 0);
	else {
		/* Become a daemon */
655 656 657 658 659
		if (chdir(TBCOREDIR)) {
			daemon(0, 0);
		} else {
			daemon(1, 0);
		}
660 661 662 663
		loginit(1, "tmcd");
	}
	info("daemon starting (version %d)\n", CURRENT_VERSION);
	info("%s\n", build_info);
Mike Hibler's avatar
Mike Hibler committed
664

Austin Clements's avatar
Austin Clements committed
665 666 667 668 669 670 671 672 673 674 675 676 677 678
	/*
	 * 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;
679 680 681
				if (debug) {
				    info("fshostid: %s\n", fshostid);
				}
Austin Clements's avatar
Austin Clements committed
682 683 684 685 686 687 688 689
			}
			else {
				error("fshostid from %s may be corrupt: %s",
				      FSHOSTID, fshostid);
			}
			fclose(fp);
		}
	}
690

691 692 693
	/*
	 * Grab our IP for security check below.
	 */
694
	if (myipaddr.s_addr == 0) {
695
#ifdef	LBS
696
		strcpy(buf, BOSSNODE);
697
#else
698 699
		if (gethostname(buf, sizeof(buf)) < 0)
			pfatal("getting hostname");
700
#endif
701 702 703 704 705 706
		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);
707 708
	}

709
	/*
710
	 * If we were given a port on the command line, don't open the
711 712 713 714
	 * alternate ports
	 */
	if (portnum != TBSERVER_PORT) {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0) {
715 716
		error("Could not make sockets!");
		exit(1);
717
	    }
718
	    num_alttcpservers = num_altudpservers = 0;
719 720 721 722 723 724
	} else {
	    if (makesockets(portnum, &udpsock, &tcpsock) < 0 ||
		makesockets(TBSERVER_PORT2, &altudpsock, &alttcpsock) < 0) {
		    error("Could not make sockets!");
		    exit(1);
	    }
725 726
	}

727 728 729 730 731 732 733 734 735 736 737 738 739
	/*
	 * Get control net info into a usable form.
	 */
	if (!inet_aton(CONTROL_NETWORK, &cnet) ||
	    !inet_aton(CONTROL_NETMASK, &cmask) ||
	    !inet_aton(JAILIPBASE, &jnet) ||
	    !inet_aton(JAILIPMASK, &jmask)) {
		error("Could not convert control net addrs/masks");
		exit(1);
	}
	cnet.s_addr &= cmask.s_addr;
	jnet.s_addr &= jmask.s_addr;

740 741 742
	signal(SIGTERM, cleanup);
	signal(SIGINT, cleanup);
	signal(SIGHUP, cleanup);
743 744
	signal(SIGUSR1, setverbose);
	signal(SIGUSR2, setverbose);
745 746 747 748

	/*
	 * Stash the pid away.
	 */
749
	mypid = getpid();
750 751
	sprintf(pidfile, "%s/tmcd.pid", _PATH_VARRUN);
	fp = fopen(pidfile, "w");
752
	if (fp != NULL) {
753
		fprintf(fp, "%d\n", mypid);
754 755 756
		(void) fclose(fp);
	}

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
	/*
	 * Change to non-root user!
	 */
	if (geteuid() == 0) {
		struct passwd	*pw;
		uid_t		uid;
		gid_t		gid;

		/*
		 * Must be a valid user of course.
		 */
		if ((pw = getpwnam(RUNASUSER)) == NULL) {
			error("invalid user: %s", RUNASUSER);
			exit(1);
		}
		uid = pw->pw_uid;
		gid = pw->pw_gid;

		if (setgroups(1, &gid)) {
			errorc("setgroups");
			exit(1);
		}
		if (setgid(gid)) {
			errorc("setgid");
			exit(1);
		}
		if (setuid(uid)) {
			errorc("setuid");
			exit(1);
		}
		info("Flipped to user/group %d/%d\n", uid, gid);
	}

790 791
	/*
	 * Now fork a set of children to handle requests. We keep the
792 793 794 795 796 797 798
	 * pool at a set level. There are 4 types of servers, each getting
	 * a different number of servers. We do it this cause otherwise
	 * we have to deal with the select storm problem; a bunch of processes
	 * select on the same set of file descriptors, and all get woken up
	 * when something comes in, then all read from the socket but only
	 * one gets it and the others go back to sleep. There are various ways
	 * to deal with this problem, but all of them are a lot more code!
799
	 */
800 801 802 803 804 805
	server_counts[0] = num_udpservers;
	server_counts[1] = num_altudpservers;
	server_counts[2] = num_alttcpservers;
	server_counts[3] = maxchildren -
		(num_udpservers + num_altudpservers + num_altudpservers);
	bzero(servers, sizeof(servers));
806

807 808
	while (1) {
		while (!killme && numchildren < maxchildren) {
809
			int which = 3;
810

811 812 813 814 815 816 817 818
			/*
			 * Find which kind of server is short one.
			 */
			for (i = 0; i < 4; i++) {
				if (server_counts[i]) {
					which = i;
					break;
				}
819
			}
820

821 822 823 824 825
			if ((pid = fork()) < 0) {
				errorc("forking server");
				goto done;
			}
			if (pid) {
826 827 828 829 830 831 832 833 834 835
				server_counts[which]--;
				/*
				 * Find free slot
				 */
				for (i = 0; i < maxchildren; i++) {
					if (!servers[i].pid)
						break;
				}
				servers[i].pid   = pid;
				servers[i].which = which;
836 837 838
				numchildren++;
				continue;
			}
839 840 841
			/* Poor way of knowing parent/child */
			numchildren = 0;
			mypid = getpid();
842

843 844 845 846
			/* Child does useful work! Never Returns! */
			signal(SIGTERM, SIG_DFL);
			signal(SIGINT, SIG_DFL);
			signal(SIGHUP, SIG_DFL);
847

848
			switch (which) {
849
			case 0: udpserver(udpsock, portnum);
850
				break;
851
			case 1: udpserver(altudpsock, TBSERVER_PORT2);
852
				break;
853
			case 2: tcpserver(alttcpsock, TBSERVER_PORT2);
854
				break;
855
			case 3: tcpserver(tcpsock, portnum);
856 857 858 859 860 861 862 863 864 865 866 867
				break;
			}
			exit(-1);
		}

		/*
		 * Parent waits.
		 */
		pid = waitpid(-1, &status, 0);
		if (pid < 0) {
			errorc("waitpid failed");
			continue;
868
		}
869 870 871
		if (WIFSIGNALED(status)) {
			error("server %d exited with signal %d!\n",
			      pid, WTERMSIG(status));
872
		}
873 874
		else if (WIFEXITED(status)) {
			error("server %d exited with status %d!\n",
875
			      pid, WEXITSTATUS(status));
876
		}
877
		numchildren--;
878 879 880 881 882 883 884 885 886 887

		/*
		 * Figure out which and what kind of server it was that died.
		 */
		for (i = 0; i < maxchildren; i++) {
			if (servers[i].pid == pid) {
				servers[i].pid = 0;
				server_counts[servers[i].which]++;
				break;
			}
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905
		}
		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;
Mike Hibler's avatar
Mike Hibler committed
906 907
	socklen_t		length;
	int			i, udpsock, tcpsock;
908

Mike Hibler's avatar
Mike Hibler committed
909
	/*
910
	 * Setup TCP socket for incoming connections.
Mike Hibler's avatar
Mike Hibler committed
911 912
	 */

913
	/* Create socket from which to read. */
Mike Hibler's avatar
Mike Hibler committed
914 915
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
916
		pfatal("opening stream socket");
917 918
	}

919
	i = 1;
Mike Hibler's avatar
Mike Hibler committed
920
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
921
		       (char *)&i, sizeof(i)) < 0)
922
		pwarning("setsockopt(SO_REUSEADDR)");;
923

924 925 926
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
927
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
928
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
929
		pfatal("binding stream socket");
930 931 932
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
Mike Hibler's avatar
Mike Hibler committed
933
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
934
		pfatal("getsockname");
935
	}
936
	if (listen(tcpsock, 128) < 0) {
937
		pfatal("listen");
938
	}
939
	info("listening on TCP port %d\n", ntohs(name.sin_port));
940

Mike Hibler's avatar
Mike Hibler committed
941 942 943 944 945 946 947
	/*
	 * Setup UDP socket
	 */

	/* Create socket from which to read. */
	udpsock = socket(AF_INET, SOCK_DGRAM, 0);
	if (udpsock < 0) {
948
		pfatal("opening dgram socket");
Mike Hibler's avatar
Mike Hibler committed
949 950 951 952 953
	}

	i = 1;
	if (setsockopt(udpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
954
		pwarning("setsockopt(SO_REUSEADDR)");;
955 956 957 958

	i = 128 * 1024;
	if (setsockopt(udpsock, SOL_SOCKET, SO_RCVBUF, &i, sizeof(i)) < 0)
		pwarning("setsockopt(SO_RCVBUF)");
959

Mike Hibler's avatar
Mike Hibler committed
960 961 962
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
963
	name.sin_port = htons((u_short) portnum);
Mike Hibler's avatar
Mike Hibler committed
964
	if (bind(udpsock, (struct sockaddr *) &name, sizeof(name))) {
965
		pfatal("binding dgram socket");
Mike Hibler's avatar
Mike Hibler committed
966 967 968 969 970
	}

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

975 976 977
	*tcpsockp = tcpsock;
	*udpsockp = udpsock;
	return 0;
978
}
979

980
/*
981
 * Listen for UDP requests. This is not a secure channel, and so this should
982 983 984
 * eventually be killed off.
 */
static void
985
udpserver(int sock, int portnum)
986 987 988
{
	char			buf[MYBUFSIZE];
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
989 990
	socklen_t		length;
	int			cc;
991
	unsigned int		nreq = 0;
992

993 994
	info("udpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
995 996 997 998 999

	/*
	 * Wait for udp connections.
	 */
	while (1) {
1000
		setproctitle("UDP %d: %u done", portnum, nreq);
1001
		length = sizeof(client);
1002
		cc = recvfrom(sock, buf, sizeof(buf) - 1,
1003 1004 1005
			      0, (struct sockaddr *)&client, &length);
		if (cc <= 0) {
			if (cc < 0)
1006 1007
				errorc("Reading UDP request");
			error("UDP Connection aborted\n");
1008
			continue;
1009
		}
1010
		buf[cc] = '\0';
1011
		handle_request(sock, &client, buf, cc, 0);
1012
		nreq++;
1013 1014 1015 1016
	}
	exit(1);
}

1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
int
tmcd_accept(int sock, struct sockaddr *addr, socklen_t *addrlen, int ms)
{
	int	newsock;

	if ((newsock = accept(sock, addr, addrlen)) < 0)
		return -1;

	/*
	 * Set timeout value to keep us from hanging due to a
	 * malfunctioning or malicious client.
	 */
	if (ms > 0) {
		struct timeval tv;

		tv.tv_sec = ms / 1000;
		tv.tv_usec = (ms % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_RCVTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_RCVTIMEO");
		}
	}

	return newsock;
}

1043
/*
1044
 * Listen for TCP requests.
1045 1046
 */
static void
1047
tcpserver(int sock, int portnum)
1048
{
1049
	char			buf[MAXTMCDPACKET];
1050
	struct sockaddr_in	client;
Mike Hibler's avatar
Mike Hibler committed
1051 1052
	socklen_t		length;
	int			cc, newsock;
1053
	unsigned int		nreq = 0;
1054
	struct timeval		tv;
1055

1056 1057
	info("tcpserver starting: pid=%d sock=%d portnum=%d\n",
	     mypid, sock, portnum);
1058 1059 1060 1061 1062

	/*
	 * Wait for TCP connections.
	 */
	while (1) {
1063
		setproctitle("TCP %d: %u done", portnum, nreq);
1064
		length  = sizeof(client);
1065 1066
		newsock = ACCEPT(sock, (struct sockaddr *)&client, &length,
				 readtimo);
1067
		if (newsock < 0) {
1068
			errorc("accepting TCP connection");
1069
			continue;
1070
		}
Mike Hibler's avatar
Mike Hibler committed
1071

1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085
		/*
		 * Set write timeout value to keep us from hanging due to a
		 * malfunctioning or malicious client.
		 * NOTE: ACCEPT function sets read timeout.
		 */
		tv.tv_sec = writetimo / 1000;
		tv.tv_usec = (writetimo % 1000) * 1000;
		if (setsockopt(newsock, SOL_SOCKET, SO_SNDTIMEO,
			       &tv, sizeof(tv)) < 0) {
			errorc("setting SO_SNDTIMEO");
			CLOSE(newsock);
			continue;
		}

Mike Hibler's avatar
Mike Hibler committed
1086
		/*
1087
		 * Read in the command request.
Mike Hibler's avatar
Mike Hibler committed
1088
		 */
1089
		if ((cc = READ(newsock, buf, sizeof(buf) - 1)) <= 0) {
1090 1091 1092 1093 1094 1095
			if (cc < 0) {
				if (errno == EWOULDBLOCK)
					errorc("Timeout reading TCP request");
				else
					errorc("Error reading TCP request");
			}
1096 1097
			error("TCP connection aborted\n");
			CLOSE(newsock);
1098
			continue;
1099
		}
1100
		buf[cc] = '\0';
1101
		handle_request(newsock, &client, buf, cc, 1);
1102
		CLOSE(newsock);
1103
		nreq++;
1104 1105 1106
	}
	exit(1);
}
Mike Hibler's avatar
Mike Hibler committed
1107

1108 1109 1110
//#define error(x...)	fprintf(stderr, ##x)
//#define info(x...)	fprintf(stderr, ##x)

1111
static int
1112
handle_request(int sock, struct sockaddr_in *client, char *rdata, int rdatalen, int istcp)
1113 1114
{
	struct sockaddr_in redirect_client;
1115
	int		   redirect = 0;
1116
	char		   buf[BUFSIZ], *bp, *cp, *ordata;
1117 1118
	char		   privkeybuf[PRIVKEY_LEN];
	char		   *privkey = (char *) NULL;
1119
	int		   i, overbose = 0, err = 0;
1120
	int		   version = DEFAULT_VERSION;
1121 1122
	tmcdreq_t	   tmcdreq, *reqp = &tmcdreq;

1123
	byteswritten = 0;
1124 1125 1126 1127 1128
#ifdef	WITHSSL
	cp = (istcp ? (isssl ? "SSL" : "TCP") : "UDP");
#else
	cp = (istcp ? "TCP" : "UDP");
#endif
1129
	setproctitle("%s: %s", inet_ntoa(client->sin_addr), cp);
1130

1131 1132 1133 1134
	/*
	 * Init the req structure.
	 */
	bzero(reqp, sizeof(*reqp));
Mike Hibler's avatar
Mike Hibler committed
1135

1136
	/*
1137
	 * Look for special tags.
1138
	 */
1139
	bp = ordata = rdata;
1140
	while ((bp = strsep(&rdata, " ")) != NULL) {
1141
		/*
1142
		 * Look for PRIVKEY.
1143
		 */
1144 1145 1146 1147 1148
		if (sscanf(bp, "PRIVKEY=%" XSTRINGIFY(PRIVKEY_LEN) "s", buf)) {
			if (strlen(buf) < 16) {
				info("tmcd client provided short privkey");
				goto skipit;
			}
1149 1150 1151 1152 1153 1154 1155
			for (i = 0; i < strlen(buf); i++){
				if (! isxdigit(buf[i])) {
					info("tmcd client provided invalid "
					     "characters in privkey");
					goto skipit;
				}
			}