]> andersk Git - moira.git/blame - lib/mr_connect.c
Command line printer manipulation client, and build goo.
[moira.git] / lib / mr_connect.c
CommitLineData
fa59b86f 1/* $Id$
e2a67c78 2 *
7ac48069 3 * This routine is part of the client library. It handles
4 * creating a connection to the moira server.
5eaef520 5 *
7ac48069 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>.
e2a67c78 9 */
10
babbc197 11#include <mit-copyright.h>
7ac48069 12#include <moira.h>
8defc06b 13#include <moira_site.h>
7ac48069 14#include "mr_private.h"
15
85330553 16#include <sys/types.h>
85330553 17
7ac48069 18#include <errno.h>
a43ce477 19#include <stdlib.h>
7ac48069 20#include <string.h>
533bacb3 21
22#ifdef HAVE_UNISTD_H
85330553 23#include <unistd.h>
533bacb3 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
7ac48069 37
ea0caf4a 38#ifdef HAVE_HESIOD
a43ce477 39#include <hesiod.h>
1215e704 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
ea0caf4a 46#endif
05fb8281 47
7ac48069 48RCSID("$Header$");
49
533bacb3 50#define DEFAULT_SERV "moira_db"
51#define DEFAULT_PORT 775
52
85330553 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";
e2a67c78 89
90/*
85330553 91 * Open a connection to the moira server. Looks for the server name
5450e3a1 92 * 1) passed as an argument, 2) in environment variable, 3) by hesiod
85330553 93 * 4) compiled in default
e2a67c78 94 */
95
5eaef520 96int mr_connect(char *server)
e2a67c78 97{
85330553 98 char *port, **pp, *sbuf = NULL;
5eaef520 99
5eaef520 100 if (_mr_conn)
101 return MR_ALREADY_CONNECTED;
85330553 102 if (!mr_inited)
103 mr_init();
5eaef520 104
105 if (!server || (strlen(server) == 0))
106 server = getenv("MOIRASERVER");
5450e3a1 107
ea0caf4a 108#ifdef HAVE_HESIOD
5eaef520 109 if (!server || (strlen(server) == 0))
110 {
111 pp = hes_resolve("moira", "sloc");
112 if (pp)
113 server = *pp;
5450e3a1 114 }
a43ce477 115#endif
5450e3a1 116
5eaef520 117 if (!server || (strlen(server) == 0))
118 server = MOIRA_SERVER;
5450e3a1 119
85330553 120 if (strchr(server, ':'))
5eaef520 121 {
85330553 122 int len = strcspn(server, ":");
123 sbuf = malloc(len + 1);
124 strncpy(sbuf, server, len);
b59b20cd 125 sbuf[len] = '\0';
85330553 126 port = strchr(server, ':') + 1;
b59b20cd 127 server = sbuf;
85330553 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
85330553 137 return MR_SUCCESS;
138}
139
140int mr_connect_internal(char *server, char *port)
141{
533bacb3 142 int size, more;
85330553 143 struct sockaddr_in target;
144 struct hostent *shost;
145 char actualresponse[53];
533bacb3 146 char *host = NULL;
147 int fd = SOCKET_ERROR;
148 int ok = 0;
07fb8582 149 int on = 1; /* Value variable for setsockopt() */
85330553 150
1215e704 151#if defined(_WIN32) && defined(HAVE_HESIOD)
152 shost = rgethostbyname(server);
153#else
85330553 154 shost = gethostbyname(server);
1215e704 155#endif
85330553 156 if (!shost)
533bacb3 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);
85330553 163
164 if (port[0] == '#')
533bacb3 165 target.sin_port = htons((unsigned short)atoi(port + 1));
85330553 166 else
167 {
168 struct servent *s;
533bacb3 169 target.sin_port = 0;
85330553 170 s = getservbyname(port, "tcp");
171 if (s)
172 target.sin_port = s->s_port;
88a72f00 173#ifdef HAVE_HESIOD
533bacb3 174 if (!target.sin_port)
88a72f00 175 {
176 s = hes_getservbyname(port, "tcp");
177 if (s)
178 target.sin_port = s->s_port;
88a72f00 179 }
88a72f00 180#endif
533bacb3 181 if (!target.sin_port && !strcasecmp(port, DEFAULT_SERV))
182 target.sin_port = htons(DEFAULT_PORT);
183 if (!target.sin_port)
184 goto cleanup;
5450e3a1 185 }
186
85330553 187 fd = socket(AF_INET, SOCK_STREAM, 0);
188 if (fd < 0)
533bacb3 189 goto cleanup;
85330553 190
07fb8582 191 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(int)) < 0)
192 goto cleanup;
193
85330553 194 if (connect(fd, (struct sockaddr *)&target, sizeof(target)) < 0)
533bacb3 195 goto cleanup;
05fb8281 196
85330553 197 /* Do magic mrgdb initialization */
533bacb3 198 size = send(fd, challenge, sizeof(challenge), 0);
85330553 199 if (size != sizeof(challenge))
533bacb3 200 goto cleanup;
85330553 201 for (size = 0; size < sizeof(actualresponse); size += more)
202 {
533bacb3 203 more = recv(fd, actualresponse + size, sizeof(actualresponse) - size, 0);
12cbd6bd 204 if (more <= 0)
85330553 205 break;
206 }
207 if (size != sizeof(actualresponse))
533bacb3 208 goto cleanup;
85330553 209 if (memcmp(actualresponse, response, sizeof(actualresponse)))
533bacb3 210 goto cleanup;
211
212 ok = 1;
213 mr_server_host = host;
214
215 cleanup:
216 if (!ok)
85330553 217 {
533bacb3 218 if (host)
219 free(host);
220 if (fd != SOCKET_ERROR)
221 closesocket(fd);
85330553 222 return 0;
223 }
85330553 224 /* You win */
225 return fd;
e2a67c78 226}
5eaef520 227
228int mr_disconnect(void)
e2a67c78 229{
5eaef520 230 CHECK_CONNECTED;
533bacb3 231 closesocket(_mr_conn);
85330553 232 _mr_conn = 0;
5eaef520 233 free(mr_server_host);
85330553 234 mr_server_host = NULL;
235 return MR_SUCCESS;
e2a67c78 236}
237
5eaef520 238int mr_host(char *host, int size)
05fb8281 239{
5eaef520 240 CHECK_CONNECTED;
05fb8281 241
5eaef520 242 /* If we are connected, mr_server_host points to a valid string. */
243 strncpy(host, mr_server_host, size);
85330553 244 host[size - 1] = '\0';
245 return MR_SUCCESS;
05fb8281 246}
247
5eaef520 248int mr_noop(void)
e2a67c78 249{
5eaef520 250 int status;
85330553 251 mr_params params, reply;
5eaef520 252
253 CHECK_CONNECTED;
85330553 254 params.u.mr_procno = MR_NOOP;
255 params.mr_argc = 0;
256 params.mr_argl = NULL;
257 params.mr_argv = NULL;
5eaef520 258
85330553 259 if ((status = mr_do_call(&params, &reply)) == MR_SUCCESS)
260 status = reply.u.mr_status;
5eaef520 261
262 mr_destroy_reply(reply);
263
264 return status;
6c00b7c4 265}
85330553 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));
1215e704 276 sin.sin_family = AF_INET;
85330553 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
1215e704 286#ifndef HAVE_HESIOD
85330553 287 return -1;
1215e704 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 */
85330553 297 }
1215e704 298
85330553 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 {
533bacb3 304 closesocket(s);
85330553 305 return -1;
306 }
307 if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
308 {
533bacb3 309 closesocket(s);
85330553 310 return -1;
311 }
312 if (listen(s, 5) < 0)
313 {
533bacb3 314 closesocket(s);
85330553 315 return -1;
316 }
317
318 return s;
319}
320
12cbd6bd 321/* mr_accept returns -1 on accept() error, 0 on bad connection,
322 or connection fd on success */
323
85330553 324int mr_accept(int s, struct sockaddr_in *sin)
325{
78eea87c 326 int conn = -1, addrlen = sizeof(struct sockaddr_in), nread, status;
767253c8 327 char *buf = NULL;
85330553 328
78eea87c 329 while (conn < 0)
330 {
331 conn = accept(s, (struct sockaddr *)sin, &addrlen);
f237ecc9 332 if (conn < 0 && errno != EINTR
333#ifdef ERESTART
334 && errno != ERESTART
4db344f0 335#endif
336#ifdef ECONNABORTED
337 && errno != ECONNABORTED
f237ecc9 338#endif
339 )
78eea87c 340 return -1;
341 }
85330553 342
12cbd6bd 343 do
344 status = mr_cont_accept(conn, &buf, &nread);
345 while (status == -1);
85330553 346
12cbd6bd 347 return status;
348}
85330553 349
88381def 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
12cbd6bd 353int mr_cont_accept(int conn, char **buf, int *nread)
354{
355 long len, more;
356
357 if (!*buf)
85330553 358 {
12cbd6bd 359 char lbuf[4];
533bacb3 360 if (recv(conn, lbuf, 4, 0) != 4)
12cbd6bd 361 {
533bacb3 362 closesocket(conn);
88381def 363 return 0;
12cbd6bd 364 }
365 getlong(lbuf, len);
366 len += 4;
367
f1291813 368 if (len < 58 || len > 1000)
369 {
370 closesocket(conn);
371 return 0;
372 }
373
12cbd6bd 374 *buf = malloc(len);
f1291813 375 if (!*buf)
12cbd6bd 376 {
533bacb3 377 closesocket(conn);
12cbd6bd 378 return 0;
379 }
380 putlong(*buf, len);
381 *nread = 4;
85330553 382 return -1;
383 }
12cbd6bd 384 else
385 getlong(*buf, len);
85330553 386
533bacb3 387 more = recv(conn, *buf + *nread, len - *nread, 0);
12cbd6bd 388
f1291813 389 switch (more)
85330553 390 {
f1291813 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;
85330553 410 }
411
12cbd6bd 412 if (memcmp(*buf + 4, challenge + 4, 34))
85330553 413 {
533bacb3 414 closesocket(conn);
12cbd6bd 415 free(*buf);
85330553 416 return 0;
417 }
418
419 /* good enough */
12cbd6bd 420 free(*buf);
85330553 421
533bacb3 422 if (send(conn, response, sizeof(response), 0) != sizeof(response))
85330553 423 {
533bacb3 424 closesocket(conn);
12cbd6bd 425 return 0;
85330553 426 }
427 return conn;
428}
This page took 0.218227 seconds and 5 git commands to generate.