source: trunk/third/moira/lib/mr_connect.c @ 26024

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