capserver.c 6.68 KB
Newer Older
Leigh B. Stoller's avatar
Leigh B. Stoller committed
1 2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
Leigh B. Stoller's avatar
Leigh B. Stoller committed
4
 * All rights reserved.
5 6 7 8 9 10 11 12 13 14 15 16 17 18
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <assert.h>
#include <stdarg.h>
19
#include <errno.h>
20
#include <mysql/mysql.h>
21
#include <sys/time.h>
22
#include <grp.h>
23
#include "capdecls.h"
24
#include "config.h"
25
#include "tbdb.h"
26 27 28 29 30

#define TESTMODE

static int	debug = 0;
static int	portnum = SERVERPORT;
31
static gid_t	admingid;
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

char *usagestr = 
 "usage: capserver [-d] [-p #]\n"
 " -d              Turn on debugging.\n"
 " -p portnum	   Specify a port number to listen on.\n"
 "\n";

void
usage()
{
	fprintf(stderr, usagestr);
	exit(1);
}

int
main(int argc, char **argv)
{
	MYSQL_RES		*res;	
50
	MYSQL_ROW		row;
51 52 53
	int			tcpsock, ch;
	int			length, i, err = 0;
	struct sockaddr_in	name;
54
	struct timeval		timeout;
55
	struct group		*group;
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

	while ((ch = getopt(argc, argv, "dp:")) != -1)
		switch(ch) {
		case 'p':
			portnum = atoi(optarg);
			break;
		case 'd':
			debug++;
			break;
		case 'h':
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	if (argc)
		usage();

76
	openlog("capserver", LOG_PID, LOG_TESTBED);
77 78 79 80 81
	syslog(LOG_NOTICE, "daemon starting");

	if (!debug)
		(void)daemon(0, 0);

82 83 84 85 86
	if (!dbinit()) {
		syslog(LOG_ERR, "Could not connect to DB!");
		exit(1);
	}

87 88 89 90 91 92 93 94 95
	/*
	 * Grab the GID for the default group.
	 */
	if ((group = getgrnam(TBADMINGROUP)) == NULL) {
		syslog(LOG_ERR, "Getting GID for %s", TBADMINGROUP);
		exit(1);
	}
	admingid = group->gr_gid;

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	/*
	 * Setup TCP socket
	 */

	/* Create socket from which to read. */
	tcpsock = socket(AF_INET, SOCK_STREAM, 0);
	if (tcpsock < 0) {
		syslog(LOG_ERR, "opening stream socket: %m");
		exit(1);
	}

	i = 1;
	if (setsockopt(tcpsock, SOL_SOCKET, SO_REUSEADDR,
		       (char *)&i, sizeof(i)) < 0)
		syslog(LOG_ERR, "control setsockopt: %m");;
	
	/* Create name. */
	name.sin_family = AF_INET;
	name.sin_addr.s_addr = INADDR_ANY;
	name.sin_port = htons((u_short) portnum);
	if (bind(tcpsock, (struct sockaddr *) &name, sizeof(name))) {
		syslog(LOG_ERR, "binding stream socket: %m");
		exit(1);
	}
	/* Find assigned port value and print it out. */
	length = sizeof(name);
	if (getsockname(tcpsock, (struct sockaddr *) &name, &length)) {
		syslog(LOG_ERR, "getting socket name: %m");
		exit(1);
	}
126
	if (listen(tcpsock, 40) < 0) {
127 128 129 130 131 132 133 134
		syslog(LOG_ERR, "listening on socket: %m");
		exit(1);
	}
	syslog(LOG_NOTICE, "listening on TCP port %d", ntohs(name.sin_port));

	while (1) {
		struct sockaddr_in client;
		int		   clientsock, length = sizeof(client);
135
		int		   cc, port;
136
		whoami_t	   whoami;
137
		unsigned char	   buf[BUFSIZ], node_id[64];
138
		secretkey_t        secretkey;
139
		tipowner_t	   tipown;
140 141
		void		  *reply = &tipown;
		size_t		   reply_size = sizeof(tipown);
142 143 144 145

		if ((clientsock = accept(tcpsock,
					 (struct sockaddr *)&client,
					 &length)) < 0) {
146 147 148 149 150 151
			if (errno == ECONNABORTED) {
				syslog(LOG_ERR, "accept failed: %m; "
				       "continuing");
				continue;
			}
			syslog(LOG_ERR, "accept failed: %m; exiting");
152 153
			exit(1);
		}
154 155 156 157 158 159 160 161 162 163 164
		port = ntohs(client.sin_port);
		syslog(LOG_INFO, "%s connected from port %d",
		       inet_ntoa(client.sin_addr), port);

		/*
		 * Check port number of sender. Must be a reserved port.
		 */
		if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED / 2) {
			syslog(LOG_ERR, "Illegal port! Ignoring.");
			goto done;
		}
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181

		/*
		 * Set timeouts
		 */
		timeout.tv_sec  = 6;
		timeout.tv_usec = 0;
		
		if (setsockopt(clientsock, SOL_SOCKET, SO_RCVTIMEO,
			       &timeout, sizeof(timeout)) < 0) {
			syslog(LOG_ERR, "SO_RCVTIMEO failed: %m");
			goto done;
		}
		if (setsockopt(clientsock, SOL_SOCKET, SO_SNDTIMEO,
			       &timeout, sizeof(timeout)) < 0) {
			syslog(LOG_ERR, "SO_SNDTIMEO failed: %m");
			goto done;
		}
182 183 184 185 186 187 188
		
		/*
		 * Read in the whoami info.
		 */
		if ((cc = read(clientsock, &whoami, sizeof(whoami))) <= 0) {
			if (cc < 0)
				syslog(LOG_ERR, "Reading request: %m");
189
			syslog(LOG_ERR, "Connection aborted (read)");
190 191 192
			goto done;
		}
		if (cc != sizeof(whoami)) {
193
			syslog(LOG_ERR, "Wrong byte count (read)!");
194 195 196 197 198 199 200 201 202
			goto done;
		}

		/*
		 * Make sure there is an entry for this tipline in
		 * the DB. If not, we just drop the info with an error
		 * message in the log file. Local tip will still work but
		 * remote tip will not.
		 */
203
		res = mydb_query("select server,node_id,portnum from tiplines "
204
				 "where tipname='%s'",
205
				 3, whoami.name);
206 207
		if (!res) {
			syslog(LOG_ERR, "DB Error getting tiplines for %s!",
208
			       whoami.name);
209 210 211 212
			goto done;
		}
		if ((int)mysql_num_rows(res) == 0) {
			syslog(LOG_ERR, "No tipline info for %s!",
213
			       whoami.name);
214 215 216
			mysql_free_result(res);
			goto done;
		}
217 218
		row = mysql_fetch_row(res);
		strcpy(node_id, row[1]);
219 220
		port = -1;
		sscanf(row[2], "%d", &port);
221 222 223 224 225 226
		mysql_free_result(res);

		/*
		 * Figure out current owner. Might not be a reserved node,
		 * in which case set it to root/wheel by default. 
		 */
227 228 229 230 231
		res = mydb_query("select g.unix_gid from reserved as r "
				 "left join experiments as e on "
				 " r.pid=e.pid and r.eid=e.eid "
				 "left join groups as g on "
				 " g.pid=e.pid and g.gid=e.gid "
232 233 234 235 236 237 238 239 240 241
				 "where r.node_id='%s'",
				 1, node_id);

		if (!res) {
			syslog(LOG_ERR, "DB Error getting info for %s/%s!",
			       node_id, whoami.name);
			goto done;
		}
		if ((int)mysql_num_rows(res)) {
			row = mysql_fetch_row(res);
242

243
			tipown.uid = 0;
244 245 246 247
			if (row[0])
				tipown.gid = atoi(row[0]);
			else
				tipown.gid = admingid;
248 249 250 251 252 253
		}
		else {
			/*
			 * Default to root/root.
			 */
			tipown.uid = 0;
254
			tipown.gid = admingid;
255
		}
256 257
		mysql_free_result(res);

258 259 260 261
		if (whoami.portnum == -1) {
			reply = &port;
			reply_size = sizeof(port);
		}
262 263 264
		/*
		 * Update the DB.
		 */
265 266 267 268 269 270
		else if (! mydb_update("update tiplines set portnum=%d, "
				       "keylen=%d, keydata='%s' "
				       "where tipname='%s'", 
				       whoami.portnum,
				       whoami.key.keylen, whoami.key.key,
				       whoami.name)) {
271
			syslog(LOG_ERR, "DB Error updating tiplines for %s!",
272
			       whoami.name);
273 274
			goto done;
		}
275 276 277 278

		/*
		 * And now send the reply.
		 */
279
		if ((cc = write(clientsock, reply, reply_size)) <= 0) {
280 281 282 283 284
			if (cc < 0)
				syslog(LOG_ERR, "Writing reply: %m");
			syslog(LOG_ERR, "Connection aborted (write)");
			goto done;
		}
285
		if (cc != reply_size) {
286 287 288 289 290 291 292 293
			syslog(LOG_ERR, "Wrong byte count (write)!");
			goto done;
		}

		syslog(LOG_INFO,
		       "Tipline %s/%s, Port %d, Keylen %d, Key %s, Group %d\n",
		       node_id, whoami.name, whoami.portnum,
		       whoami.key.keylen, whoami.key.key, tipown.gid);
294 295 296 297 298 299 300 301
	done:
		close(clientsock);
	}
	close(tcpsock);
	syslog(LOG_NOTICE, "daemon terminating");
	exit(0);
}