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;
Timothy Stack's avatar
   
Timothy Stack committed
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.
		 */
Timothy Stack's avatar
   
Timothy Stack committed
203
		res = mydb_query("select server,node_id,portnum from tiplines "
204
				 "where tipname='%s'",
Timothy Stack's avatar
   
Timothy Stack committed
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]);
Timothy Stack's avatar
   
Timothy Stack committed
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);

Timothy Stack's avatar
   
Timothy Stack committed
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.
		 */
Timothy Stack's avatar
   
Timothy Stack committed
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;
		}
Timothy Stack's avatar
   
Timothy Stack committed
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);
}