]> andersk Git - moira.git/blob - lib/mr_connect.c
Command line printer manipulation client, and build goo.
[moira.git] / lib / mr_connect.c
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  */
44 struct hostent * WINAPI rgethostbyname(char *name);
45 #endif
46 #endif
47
48 RCSID("$Header$");
49
50 #define DEFAULT_SERV "moira_db"
51 #define DEFAULT_PORT 775
52
53 int _mr_conn = 0;
54 static 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
87 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";
88 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";
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
96 int 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
140 int 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
228 int 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
238 int 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
248 int 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
270 int 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
324 int 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
353 int 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.064174 seconds and 5 git commands to generate.