]> andersk Git - moira.git/blame_incremental - lib/mr_connect.c
Command line printer manipulation client, and build goo.
[moira.git] / lib / mr_connect.c
... / ...
CommitLineData
1/* $Id$
2 *
3 * This routine is part of the client library. It handles
4 * creating a connection to the moira server.
5 *
6 * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
7 * For copying and distribution information, please see the file
8 * <mit-copyright.h>.
9 */
10
11#include <mit-copyright.h>
12#include <moira.h>
13#include <moira_site.h>
14#include "mr_private.h"
15
16#include <sys/types.h>
17
18#include <errno.h>
19#include <stdlib.h>
20#include <string.h>
21
22#ifdef HAVE_UNISTD_H
23#include <unistd.h>
24#endif
25
26#ifndef _WIN32
27#include <sys/socket.h>
28#include <netinet/in.h>
29#include <netdb.h>
30#ifndef closesocket
31#define closesocket close
32#endif
33#ifndef SOCKET_ERROR
34#define SOCKET_ERROR -1
35#endif
36#endif
37
38#ifdef HAVE_HESIOD
39#include <hesiod.h>
40#ifdef _WIN32
41/* This is declared in wshelper's resolv.h, but the definition of
42 * the putlong macro conflicts with Moira's
43 */
44struct hostent * WINAPI rgethostbyname(char *name);
45#endif
46#endif
47
48RCSID("$Header$");
49
50#define DEFAULT_SERV "moira_db"
51#define DEFAULT_PORT 775
52
53int _mr_conn = 0;
54static char *mr_server_host = NULL;
55
56/* mrgdb compatibility magic
57
58 The data looks like this:
59
60 client -> server
61 00000036 [length of rest of packet]
62 00000004 [number of fields]
63 01 01 01 01 [types of fields: 4 strings]
64 "server_id\0parms\0host\0user\0" [field names]
65 00000001 [length of null-terminated server_id]
66 "\0" [server_id: ignored anyway]
67 00000001 [length of null-terminated parms]
68 "\0" [parms: ignored anyway]
69 00000001 [length of null-terminated client host]
70 "\0" [host: ignored anyway]
71 00000001 [length of null-terminated client name]
72 "\0" [user: ignored anyway]
73
74 server -> client
75 00000031 [length of rest of packet]
76 00000003 [number of fields]
77 00 01 01 [types of fields: int and 2 strings]
78 "disposition\0server_id\0parms\0" [field names]
79 00000001 [GDB_ACCEPTED]
80 00000001 [length of null-terminated server_id]
81 "\0" [server_id: ignored anyway]
82 00000001 [length of null-terminated parms]
83 "\0" [parms: ignored anyway]
84
85*/
86
87static char challenge[58] = "\0\0\0\066\0\0\0\004\001\001\001\001server_id\0parms\0host\0user\0\0\0\0\001\0\0\0\0\001\0\0\0\0\001\0\0\0\0\001\0";
88static char response[53] = "\0\0\0\061\0\0\0\003\0\001\001disposition\0server_id\0parms\0\0\0\0\001\0\0\0\001\0\0\0\0\001\0";
89
90/*
91 * Open a connection to the moira server. Looks for the server name
92 * 1) passed as an argument, 2) in environment variable, 3) by hesiod
93 * 4) compiled in default
94 */
95
96int mr_connect(char *server)
97{
98 char *port, **pp, *sbuf = NULL;
99
100 if (_mr_conn)
101 return MR_ALREADY_CONNECTED;
102 if (!mr_inited)
103 mr_init();
104
105 if (!server || (strlen(server) == 0))
106 server = getenv("MOIRASERVER");
107
108#ifdef HAVE_HESIOD
109 if (!server || (strlen(server) == 0))
110 {
111 pp = hes_resolve("moira", "sloc");
112 if (pp)
113 server = *pp;
114 }
115#endif
116
117 if (!server || (strlen(server) == 0))
118 server = MOIRA_SERVER;
119
120 if (strchr(server, ':'))
121 {
122 int len = strcspn(server, ":");
123 sbuf = malloc(len + 1);
124 strncpy(sbuf, server, len);
125 sbuf[len] = '\0';
126 port = strchr(server, ':') + 1;
127 server = sbuf;
128 }
129 else
130 port = strchr(MOIRA_SERVER, ':') + 1;
131
132 _mr_conn = mr_connect_internal(server, port);
133 free(sbuf);
134 if (!_mr_conn)
135 return MR_CANT_CONNECT;
136
137 return MR_SUCCESS;
138}
139
140int mr_connect_internal(char *server, char *port)
141{
142 int size, more;
143 struct sockaddr_in target;
144 struct hostent *shost;
145 char actualresponse[53];
146 char *host = NULL;
147 int fd = SOCKET_ERROR;
148 int ok = 0;
149 int on = 1; /* Value variable for setsockopt() */
150
151#if defined(_WIN32) && defined(HAVE_HESIOD)
152 shost = rgethostbyname(server);
153#else
154 shost = gethostbyname(server);
155#endif
156 if (!shost)
157 goto cleanup;
158
159 /* Get the host info in case some library decides to clobber shost. */
160 memcpy(&target.sin_addr, shost->h_addr, shost->h_length);
161 target.sin_family = shost->h_addrtype;
162 host = strdup(shost->h_name);
163
164 if (port[0] == '#')
165 target.sin_port = htons((unsigned short)atoi(port + 1));
166 else
167 {
168 struct servent *s;
169 target.sin_port = 0;
170 s = getservbyname(port, "tcp");
171 if (s)
172 target.sin_port = s->s_port;
173#ifdef HAVE_HESIOD
174 if (!target.sin_port)
175 {
176 s = hes_getservbyname(port, "tcp");
177 if (s)
178 target.sin_port = s->s_port;
179 }
180#endif
181 if (!target.sin_port && !strcasecmp(port, DEFAULT_SERV))
182 target.sin_port = htons(DEFAULT_PORT);
183 if (!target.sin_port)
184 goto cleanup;
185 }
186
187 fd = socket(AF_INET, SOCK_STREAM, 0);
188 if (fd < 0)
189 goto cleanup;
190
191 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(int)) < 0)
192 goto cleanup;
193
194 if (connect(fd, (struct sockaddr *)&target, sizeof(target)) < 0)
195 goto cleanup;
196
197 /* Do magic mrgdb initialization */
198 size = send(fd, challenge, sizeof(challenge), 0);
199 if (size != sizeof(challenge))
200 goto cleanup;
201 for (size = 0; size < sizeof(actualresponse); size += more)
202 {
203 more = recv(fd, actualresponse + size, sizeof(actualresponse) - size, 0);
204 if (more <= 0)
205 break;
206 }
207 if (size != sizeof(actualresponse))
208 goto cleanup;
209 if (memcmp(actualresponse, response, sizeof(actualresponse)))
210 goto cleanup;
211
212 ok = 1;
213 mr_server_host = host;
214
215 cleanup:
216 if (!ok)
217 {
218 if (host)
219 free(host);
220 if (fd != SOCKET_ERROR)
221 closesocket(fd);
222 return 0;
223 }
224 /* You win */
225 return fd;
226}
227
228int mr_disconnect(void)
229{
230 CHECK_CONNECTED;
231 closesocket(_mr_conn);
232 _mr_conn = 0;
233 free(mr_server_host);
234 mr_server_host = NULL;
235 return MR_SUCCESS;
236}
237
238int mr_host(char *host, int size)
239{
240 CHECK_CONNECTED;
241
242 /* If we are connected, mr_server_host points to a valid string. */
243 strncpy(host, mr_server_host, size);
244 host[size - 1] = '\0';
245 return MR_SUCCESS;
246}
247
248int mr_noop(void)
249{
250 int status;
251 mr_params params, reply;
252
253 CHECK_CONNECTED;
254 params.u.mr_procno = MR_NOOP;
255 params.mr_argc = 0;
256 params.mr_argl = NULL;
257 params.mr_argv = NULL;
258
259 if ((status = mr_do_call(&params, &reply)) == MR_SUCCESS)
260 status = reply.u.mr_status;
261
262 mr_destroy_reply(reply);
263
264 return status;
265}
266
267
268/* Server side */
269
270int mr_listen(char *port)
271{
272 struct sockaddr_in sin;
273 int s, on = 1;
274
275 memset(&sin, 0, sizeof(sin));
276 sin.sin_family = AF_INET;
277 if (port[0] == '#')
278 sin.sin_port = atoi(port + 1);
279 else
280 {
281 struct servent *s;
282 s = getservbyname(port, "tcp");
283 if (s)
284 sin.sin_port = s->s_port;
285 else
286#ifndef HAVE_HESIOD
287 return -1;
288#else
289 {
290 s = hes_getservbyname(port, "tcp");
291 if (s)
292 sin.sin_port = s->s_port;
293 else
294 return -1;
295 }
296#endif /* HAVE_HESIOD */
297 }
298
299 s = socket(AF_INET, SOCK_STREAM, 0);
300 if (s < 0)
301 return -1;
302 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)) < 0)
303 {
304 closesocket(s);
305 return -1;
306 }
307 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
308 {
309 closesocket(s);
310 return -1;
311 }
312 if (listen(s, 5) < 0)
313 {
314 closesocket(s);
315 return -1;
316 }
317
318 return s;
319}
320
321/* mr_accept returns -1 on accept() error, 0 on bad connection,
322 or connection fd on success */
323
324int mr_accept(int s, struct sockaddr_in *sin)
325{
326 int conn = -1, addrlen = sizeof(struct sockaddr_in), nread, status;
327 char *buf = NULL;
328
329 while (conn < 0)
330 {
331 conn = accept(s, (struct sockaddr *)sin, &addrlen);
332 if (conn < 0 && errno != EINTR
333#ifdef ERESTART
334 && errno != ERESTART
335#endif
336#ifdef ECONNABORTED
337 && errno != ECONNABORTED
338#endif
339 )
340 return -1;
341 }
342
343 do
344 status = mr_cont_accept(conn, &buf, &nread);
345 while (status == -1);
346
347 return status;
348}
349
350/* mr_cont_accept returns 0 if it has failed, an fd if it has succeeded,
351 or -1 if it is still making progress */
352
353int mr_cont_accept(int conn, char **buf, int *nread)
354{
355 long len, more;
356
357 if (!*buf)
358 {
359 char lbuf[4];
360 if (recv(conn, lbuf, 4, 0) != 4)
361 {
362 closesocket(conn);
363 return 0;
364 }
365 getlong(lbuf, len);
366 len += 4;
367
368 if (len < 58 || len > 1000)
369 {
370 closesocket(conn);
371 return 0;
372 }
373
374 *buf = malloc(len);
375 if (!*buf)
376 {
377 closesocket(conn);
378 return 0;
379 }
380 putlong(*buf, len);
381 *nread = 4;
382 return -1;
383 }
384 else
385 getlong(*buf, len);
386
387 more = recv(conn, *buf + *nread, len - *nread, 0);
388
389 switch (more)
390 {
391 case 0:
392 /* If we read 0 bytes, the remote end has gone away. */
393 break;
394 case -1:
395 /* If errno is EINTR, return -1 and try again, otherwise we failed. */
396 if (errno == EINTR)
397 return -1;
398 else
399 {
400 closesocket(conn);
401 free(*buf);
402 return 0;
403 }
404 break;
405 default:
406 *nread += more;
407 if (*nread != len)
408 return -1;
409 break;
410 }
411
412 if (memcmp(*buf + 4, challenge + 4, 34))
413 {
414 closesocket(conn);
415 free(*buf);
416 return 0;
417 }
418
419 /* good enough */
420 free(*buf);
421
422 if (send(conn, response, sizeof(response), 0) != sizeof(response))
423 {
424 closesocket(conn);
425 return 0;
426 }
427 return conn;
428}
This page took 0.036269 seconds and 5 git commands to generate.