]> andersk Git - moira.git/blobdiff - lib/mr_connect.c
Command line printer manipulation client, and build goo.
[moira.git] / lib / mr_connect.c
index f5e27177b6dce6e9a29e69abe40d380a1746c1c4..9dc690e1c98a31b9491b52ba61f8476072f9a809 100644 (file)
-/*
- *     $Source$
- *     $Author$
- *     $Header$
+/* $Id$
+ *
+ * This routine is part of the client library.  It handles
+ * creating a connection to the moira server.
  *
- *     Copyright (C) 1987 by the Massachusetts Institute of Technology
- *     
- *     This routine is part of the client library.  It handles
- * creating a connection to the sms server.
+ * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
  */
 
-#ifndef lint
-static char *rcsid_sms_connect_c = "$Header$";
-#endif lint
+#include <mit-copyright.h>
+#include <moira.h>
+#include <moira_site.h>
+#include "mr_private.h"
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#ifndef closesocket
+#define closesocket close
+#endif
+#ifndef SOCKET_ERROR
+#define SOCKET_ERROR -1
+#endif
+#endif
+
+#ifdef HAVE_HESIOD
+#include <hesiod.h>
+#ifdef _WIN32
+/* This is declared in wshelper's resolv.h, but the definition of
+ * the putlong macro conflicts with Moira's
+ */
+struct hostent * WINAPI rgethostbyname(char *name);
+#endif
+#endif
+
+RCSID("$Header$");
+
+#define DEFAULT_SERV "moira_db"
+#define DEFAULT_PORT 775
+
+int _mr_conn = 0;
+static char *mr_server_host = NULL;
 
-#include "sms_private.h"
+/* mrgdb compatibility magic
+
+   The data looks like this:
+
+   client -> server
+     00000036                          [length of rest of packet]
+     00000004                          [number of fields]
+     01 01 01 01                       [types of fields: 4 strings]
+     "server_id\0parms\0host\0user\0"  [field names]
+     00000001                          [length of null-terminated server_id]
+     "\0"                              [server_id: ignored anyway]
+     00000001                          [length of null-terminated parms]
+     "\0"                              [parms: ignored anyway]
+     00000001                          [length of null-terminated client host]
+     "\0"                              [host: ignored anyway]
+     00000001                          [length of null-terminated client name]
+     "\0"                              [user: ignored anyway]
+
+   server -> client
+     00000031                          [length of rest of packet]
+     00000003                          [number of fields]
+     00 01 01                          [types of fields: int and 2 strings]
+     "disposition\0server_id\0parms\0" [field names]
+     00000001                          [GDB_ACCEPTED]
+     00000001                          [length of null-terminated server_id]
+     "\0"                              [server_id: ignored anyway]
+     00000001                          [length of null-terminated parms]
+     "\0"                              [parms: ignored anyway]
+
+*/
+
+static 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";
+static 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";
 
 /*
- * Open a connection to the sms server.
+ * Open a connection to the moira server.  Looks for the server name
+ * 1) passed as an argument, 2) in environment variable, 3) by hesiod
+ * 4) compiled in default
  */
 
-int sms_connect()
+int mr_connect(char *server)
 {
-    extern int errno;
-       
-    if (!sms_inited) sms_init();
-    if (_sms_conn) return SMS_ALREADY_CONNECTED;
-               
-    /* 
-     * XXX should do a hesiod call to find the sms machine name & service
-     * number/name.
-     */
-    errno = 0;
-    _sms_conn = start_server_connection(SMS_GDB_SERV, ""); 
-    if (_sms_conn == NULL)
-       return errno;
-    if (connection_status(_sms_conn) == CON_STOPPED) {
-       register status = connection_errno(_sms_conn);
-       if (!status) status = SMS_CANT_CONNECT;
-       sms_disconnect();
-       return status;
+  char *port, **pp, *sbuf = NULL;
+
+  if (_mr_conn)
+    return MR_ALREADY_CONNECTED;
+  if (!mr_inited)
+    mr_init();
+
+  if (!server || (strlen(server) == 0))
+    server = getenv("MOIRASERVER");
+
+#ifdef HAVE_HESIOD
+  if (!server || (strlen(server) == 0))
+    {
+      pp = hes_resolve("moira", "sloc");
+      if (pp)
+       server = *pp;
+    }
+#endif
+
+  if (!server || (strlen(server) == 0))
+    server = MOIRA_SERVER;
+
+  if (strchr(server, ':'))
+    {
+      int len = strcspn(server, ":");
+      sbuf = malloc(len + 1);
+      strncpy(sbuf, server, len);
+      sbuf[len] = '\0';
+      port = strchr(server, ':') + 1;
+      server = sbuf;
     }
-    return 0;
+  else
+    port = strchr(MOIRA_SERVER, ':') + 1;
+
+  _mr_conn = mr_connect_internal(server, port);
+  free(sbuf);
+  if (!_mr_conn)
+    return MR_CANT_CONNECT;
+
+  return MR_SUCCESS;
 }
-       
-int sms_disconnect()
+
+int mr_connect_internal(char *server, char *port)
 {
-    CHECK_CONNECTED;
-    _sms_conn = sever_connection(_sms_conn);
-    return 0;
+  int size, more;
+  struct sockaddr_in target;
+  struct hostent *shost;
+  char actualresponse[53];
+  char *host = NULL;
+  int fd = SOCKET_ERROR;
+  int ok = 0;
+  int on = 1; /* Value variable for setsockopt() */
+
+#if defined(_WIN32) && defined(HAVE_HESIOD)
+  shost = rgethostbyname(server);
+#else
+  shost = gethostbyname(server);
+#endif
+  if (!shost)
+    goto cleanup;
+
+  /* Get the host info in case some library decides to clobber shost. */
+  memcpy(&target.sin_addr, shost->h_addr, shost->h_length);
+  target.sin_family = shost->h_addrtype;
+  host = strdup(shost->h_name);
+
+  if (port[0] == '#')
+    target.sin_port = htons((unsigned short)atoi(port + 1));
+  else
+    {
+      struct servent *s;
+      target.sin_port = 0;
+      s = getservbyname(port, "tcp");
+      if (s)
+       target.sin_port = s->s_port;
+#ifdef HAVE_HESIOD
+      if (!target.sin_port)
+        {
+          s = hes_getservbyname(port, "tcp");
+          if (s)
+            target.sin_port = s->s_port;
+        }
+#endif
+      if (!target.sin_port && !strcasecmp(port, DEFAULT_SERV))
+       target.sin_port = htons(DEFAULT_PORT);
+      if (!target.sin_port)
+       goto cleanup;
+    }
+
+  fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (fd < 0)
+    goto cleanup;
+
+  if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(int)) < 0)
+    goto cleanup;
+
+  if (connect(fd, (struct sockaddr *)&target, sizeof(target)) < 0)
+    goto cleanup;
+
+  /* Do magic mrgdb initialization */
+  size = send(fd, challenge, sizeof(challenge), 0);
+  if (size != sizeof(challenge))
+    goto cleanup;
+  for (size = 0; size < sizeof(actualresponse); size += more)
+    {
+      more = recv(fd, actualresponse + size, sizeof(actualresponse) - size, 0);
+      if (more <= 0)
+       break;
+    }
+  if (size != sizeof(actualresponse))
+    goto cleanup;
+  if (memcmp(actualresponse, response, sizeof(actualresponse)))
+    goto cleanup;
+
+  ok = 1;
+  mr_server_host = host;
+
+ cleanup:
+  if (!ok)
+    {
+      if (host)
+       free(host);
+      if (fd != SOCKET_ERROR)
+       closesocket(fd);
+      return 0;
+    }
+  /* You win */
+  return fd;
 }
 
-int sms_noop()
+int mr_disconnect(void)
 {
-    int status;
-    sms_params param_st;
-    struct sms_params *params = NULL;
-    struct sms_params *reply = NULL;
-
-    CHECK_CONNECTED;
-    params = &param_st;
-    params->sms_procno = SMS_NOOP;
-    params->sms_argc = 0;
-    params->sms_argl = NULL;
-    params->sms_argv = NULL;
-       
-    if ((status = sms_do_call(params, &reply)) == 0)
-       status = reply->sms_status;
-       
-    sms_destroy_reply(reply);
-
-    return status;
+  CHECK_CONNECTED;
+  closesocket(_mr_conn);
+  _mr_conn = 0;
+  free(mr_server_host);
+  mr_server_host = NULL;
+  return MR_SUCCESS;
 }
 
-/*
- * Local Variables:
- * mode: c
- * c-indent-level: 4
- * c-continued-statement-offset: 4
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * End:
- */
+int mr_host(char *host, int size)
+{
+  CHECK_CONNECTED;
+
+  /* If we are connected, mr_server_host points to a valid string. */
+  strncpy(host, mr_server_host, size);
+  host[size - 1] = '\0';
+  return MR_SUCCESS;
+}
+
+int mr_noop(void)
+{
+  int status;
+  mr_params params, reply;
+
+  CHECK_CONNECTED;
+  params.u.mr_procno = MR_NOOP;
+  params.mr_argc = 0;
+  params.mr_argl = NULL;
+  params.mr_argv = NULL;
+
+  if ((status = mr_do_call(&params, &reply)) == MR_SUCCESS)
+    status = reply.u.mr_status;
+
+  mr_destroy_reply(reply);
+
+  return status;
+}
+
+
+/* Server side */
+
+int mr_listen(char *port)
+{
+  struct sockaddr_in sin;
+  int s, on = 1;
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (port[0] == '#')
+    sin.sin_port = atoi(port + 1);
+  else
+    {
+      struct servent *s;
+      s = getservbyname(port, "tcp");
+      if (s)
+       sin.sin_port = s->s_port;
+      else
+#ifndef HAVE_HESIOD
+       return -1;
+#else
+      {
+        s = hes_getservbyname(port, "tcp");
+       if (s)
+         sin.sin_port = s->s_port;
+       else
+         return -1;
+      }
+#endif /* HAVE_HESIOD */
+    }
+  
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s < 0)
+    return -1;
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)) < 0)
+    {
+      closesocket(s);
+      return -1;
+    }
+  if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+    {
+      closesocket(s);
+      return -1;
+    }
+  if (listen(s, 5) < 0)
+    {
+      closesocket(s);
+      return -1;
+    }
+
+  return s;
+}
+
+/* mr_accept returns -1 on accept() error, 0 on bad connection,
+   or connection fd on success */
+
+int mr_accept(int s, struct sockaddr_in *sin)
+{
+  int conn = -1, addrlen = sizeof(struct sockaddr_in), nread, status;
+  char *buf = NULL;
+
+  while (conn < 0)
+    {
+      conn = accept(s, (struct sockaddr *)sin, &addrlen);
+      if (conn < 0 && errno != EINTR
+#ifdef ERESTART
+         && errno != ERESTART
+#endif
+#ifdef ECONNABORTED
+         && errno != ECONNABORTED
+#endif
+         )
+       return -1;
+    }
+
+  do
+    status = mr_cont_accept(conn, &buf, &nread);
+  while (status == -1);
+
+  return status;
+}
+
+/* mr_cont_accept returns 0 if it has failed, an fd if it has succeeded,
+   or -1 if it is still making progress */
+
+int mr_cont_accept(int conn, char **buf, int *nread)
+{
+  long len, more;
+
+  if (!*buf)
+    {
+      char lbuf[4];
+      if (recv(conn, lbuf, 4, 0) != 4)
+       {
+         closesocket(conn);
+         return 0;
+       }
+      getlong(lbuf, len);
+      len += 4;
+
+      if (len < 58 || len > 1000)
+       {
+         closesocket(conn);
+         return 0;
+       }
+
+      *buf = malloc(len);
+      if (!*buf)
+       {
+         closesocket(conn);
+         return 0;
+       }
+      putlong(*buf, len);
+      *nread = 4;
+      return -1;
+    }
+  else
+    getlong(*buf, len);
+
+  more = recv(conn, *buf + *nread, len - *nread, 0);
+
+  switch (more)
+    {
+    case 0:
+      /* If we read 0 bytes, the remote end has gone away. */
+      break;
+    case -1:
+      /* If errno is EINTR, return -1 and try again, otherwise we failed. */
+      if (errno == EINTR)
+       return -1;
+      else
+       {
+         closesocket(conn);
+         free(*buf);
+         return 0;
+       }
+      break;
+    default:
+      *nread += more;
+      if (*nread != len)
+       return -1;
+      break;
+    }
+
+  if (memcmp(*buf + 4, challenge + 4, 34))
+    {
+      closesocket(conn);
+      free(*buf);
+      return 0;
+    }
+
+  /* good enough */
+  free(*buf);
+
+  if (send(conn, response, sizeof(response), 0) != sizeof(response))
+    {
+      closesocket(conn);
+      return 0;
+    }
+  return conn;
+}
This page took 0.064176 seconds and 4 git commands to generate.