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

Revision 24319, 9.0 KB checked in by broder, 14 years ago (diff)
New Moira snapshot from SVN.
Line 
1/* $Id: mr_connect.c 3956 2010-01-05 20:56:56Z 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#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("$HeadURL: svn+ssh://svn.mit.edu/moira/trunk/moira/lib/mr_connect.c $ $Id: mr_connect.c 3956 2010-01-05 20:56:56Z zacheiss $");
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}
Note: See TracBrowser for help on using the repository browser.