]> andersk Git - moira.git/blob - lib/mr_connect.c
Turn on SO_KEEPALIVE.
[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 #endif
41
42 RCSID("$Header$");
43
44 #define DEFAULT_SERV "moira_db"
45 #define DEFAULT_PORT 775
46
47 int _mr_conn = 0;
48 static char *mr_server_host = NULL;
49
50 /* mrgdb compatibility magic
51
52    The data looks like this:
53
54    client -> server
55      00000036                           [length of rest of packet]
56      00000004                           [number of fields]
57      01 01 01 01                        [types of fields: 4 strings]
58      "server_id\0parms\0host\0user\0"   [field names]
59      00000001                           [length of null-terminated server_id]
60      "\0"                               [server_id: ignored anyway]
61      00000001                           [length of null-terminated parms]
62      "\0"                               [parms: ignored anyway]
63      00000001                           [length of null-terminated client host]
64      "\0"                               [host: ignored anyway]
65      00000001                           [length of null-terminated client name]
66      "\0"                               [user: ignored anyway]
67
68    server -> client
69      00000031                           [length of rest of packet]
70      00000003                           [number of fields]
71      00 01 01                           [types of fields: int and 2 strings]
72      "disposition\0server_id\0parms\0"  [field names]
73      00000001                           [GDB_ACCEPTED]
74      00000001                           [length of null-terminated server_id]
75      "\0"                               [server_id: ignored anyway]
76      00000001                           [length of null-terminated parms]
77      "\0"                               [parms: ignored anyway]
78
79 */
80
81 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";
82 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";
83
84 /*
85  * Open a connection to the moira server.  Looks for the server name
86  * 1) passed as an argument, 2) in environment variable, 3) by hesiod
87  * 4) compiled in default
88  */
89
90 int mr_connect(char *server)
91 {
92   char *port, **pp, *sbuf = NULL;
93
94   if (_mr_conn)
95     return MR_ALREADY_CONNECTED;
96   if (!mr_inited)
97     mr_init();
98
99   if (!server || (strlen(server) == 0))
100     server = getenv("MOIRASERVER");
101
102 #ifdef HAVE_HESIOD
103   if (!server || (strlen(server) == 0))
104     {
105       pp = hes_resolve("moira", "sloc");
106       if (pp)
107         server = *pp;
108     }
109 #endif
110
111   if (!server || (strlen(server) == 0))
112     server = MOIRA_SERVER;
113
114   if (strchr(server, ':'))
115     {
116       int len = strcspn(server, ":");
117       sbuf = malloc(len + 1);
118       strncpy(sbuf, server, len);
119       sbuf[len] = '\0';
120       port = strchr(server, ':') + 1;
121       server = sbuf;
122     }
123   else
124     port = strchr(MOIRA_SERVER, ':') + 1;
125
126   _mr_conn = mr_connect_internal(server, port);
127   free(sbuf);
128   if (!_mr_conn)
129     return MR_CANT_CONNECT;
130
131   return MR_SUCCESS;
132 }
133
134 int mr_connect_internal(char *server, char *port)
135 {
136   int size, more;
137   struct sockaddr_in target;
138   struct hostent *shost;
139   char actualresponse[53];
140   char *host = NULL;
141   int fd = SOCKET_ERROR;
142   int ok = 0;
143   int on = 1; /* Value variable for setsockopt() */
144
145   shost = gethostbyname(server);
146   if (!shost)
147     goto cleanup;
148
149   /* Get the host info in case some library decides to clobber shost. */
150   memcpy(&target.sin_addr, shost->h_addr, shost->h_length);
151   target.sin_family = shost->h_addrtype;
152   host = strdup(shost->h_name);
153
154   if (port[0] == '#')
155     target.sin_port = htons((unsigned short)atoi(port + 1));
156   else
157     {
158       struct servent *s;
159       target.sin_port = 0;
160       s = getservbyname(port, "tcp");
161       if (s)
162         target.sin_port = s->s_port;
163 #ifdef HAVE_HESIOD
164       if (!target.sin_port)
165         {
166           s = hes_getservbyname(port, "tcp");
167           if (s)
168             target.sin_port = s->s_port;
169         }
170 #endif
171       if (!target.sin_port && !strcasecmp(port, DEFAULT_SERV))
172         target.sin_port = htons(DEFAULT_PORT);
173       if (!target.sin_port)
174         goto cleanup;
175     }
176
177   fd = socket(AF_INET, SOCK_STREAM, 0);
178   if (fd < 0)
179     goto cleanup;
180
181   if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof(int)) < 0)
182     goto cleanup;
183
184   if (connect(fd, (struct sockaddr *)&target, sizeof(target)) < 0)
185     goto cleanup;
186
187   /* Do magic mrgdb initialization */
188   size = send(fd, challenge, sizeof(challenge), 0);
189   if (size != sizeof(challenge))
190     goto cleanup;
191   for (size = 0; size < sizeof(actualresponse); size += more)
192     {
193       more = recv(fd, actualresponse + size, sizeof(actualresponse) - size, 0);
194       if (more <= 0)
195         break;
196     }
197   if (size != sizeof(actualresponse))
198     goto cleanup;
199   if (memcmp(actualresponse, response, sizeof(actualresponse)))
200     goto cleanup;
201
202   ok = 1;
203   mr_server_host = host;
204
205  cleanup:
206   if (!ok)
207     {
208       if (host)
209         free(host);
210       if (fd != SOCKET_ERROR)
211         closesocket(fd);
212       return 0;
213     }
214   /* You win */
215   return fd;
216 }
217
218 int mr_disconnect(void)
219 {
220   CHECK_CONNECTED;
221   closesocket(_mr_conn);
222   _mr_conn = 0;
223   free(mr_server_host);
224   mr_server_host = NULL;
225   return MR_SUCCESS;
226 }
227
228 int mr_host(char *host, int size)
229 {
230   CHECK_CONNECTED;
231
232   /* If we are connected, mr_server_host points to a valid string. */
233   strncpy(host, mr_server_host, size);
234   host[size - 1] = '\0';
235   return MR_SUCCESS;
236 }
237
238 int mr_noop(void)
239 {
240   int status;
241   mr_params params, reply;
242
243   CHECK_CONNECTED;
244   params.u.mr_procno = MR_NOOP;
245   params.mr_argc = 0;
246   params.mr_argl = NULL;
247   params.mr_argv = NULL;
248
249   if ((status = mr_do_call(&params, &reply)) == MR_SUCCESS)
250     status = reply.u.mr_status;
251
252   mr_destroy_reply(reply);
253
254   return status;
255 }
256
257
258 /* Server side */
259
260 int mr_listen(char *port)
261 {
262   struct sockaddr_in sin;
263   int s, on = 1;
264
265   memset(&sin, 0, sizeof(sin));
266   if (port[0] == '#')
267     sin.sin_port = atoi(port + 1);
268   else
269     {
270       struct servent *s;
271       s = getservbyname(port, "tcp");
272       if (s)
273         sin.sin_port = s->s_port;
274       else
275         return -1;
276     }
277
278   s = socket(AF_INET, SOCK_STREAM, 0);
279   if (s < 0)
280     return -1;
281   if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(int)) < 0)
282     {
283       closesocket(s);
284       return -1;
285     }
286   if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
287     {
288       closesocket(s);
289       return -1;
290     }
291   if (listen(s, 5) < 0)
292     {
293       closesocket(s);
294       return -1;
295     }
296
297   return s;
298 }
299
300 /* mr_accept returns -1 on accept() error, 0 on bad connection,
301    or connection fd on success */
302
303 int mr_accept(int s, struct sockaddr_in *sin)
304 {
305   int conn = -1, addrlen = sizeof(struct sockaddr_in), nread, status;
306   char *buf = NULL;
307
308   while (conn < 0)
309     {
310       conn = accept(s, (struct sockaddr *)sin, &addrlen);
311       if (conn < 0 && errno != EINTR
312 #ifdef ERESTART
313           && errno != ERESTART
314 #endif
315           )
316         return -1;
317     }
318
319   do
320     status = mr_cont_accept(conn, &buf, &nread);
321   while (status == -1);
322
323   return status;
324 }
325
326 /* mr_cont_accept returns 0 if it has failed, an fd if it has succeeded,
327    or -1 if it is still making progress */
328
329 int mr_cont_accept(int conn, char **buf, int *nread)
330 {
331   long len, more;
332
333   if (!*buf)
334     {
335       char lbuf[4];
336       if (recv(conn, lbuf, 4, 0) != 4)
337         {
338           closesocket(conn);
339           return 0;
340         }
341       getlong(lbuf, len);
342       len += 4;
343
344       *buf = malloc(len);
345       if (!*buf || len < 58)
346         {
347           closesocket(conn);
348           free(*buf);
349           return 0;
350         }
351       putlong(*buf, len);
352       *nread = 4;
353       return -1;
354     }
355   else
356     getlong(*buf, len);
357
358   more = recv(conn, *buf + *nread, len - *nread, 0);
359
360   if (more == -1 && errno != EINTR)
361     {
362       closesocket(conn);
363       free(*buf);
364       return 0;
365     }
366
367   *nread += more;
368
369   if (*nread != len)
370     return -1;
371
372   if (memcmp(*buf + 4, challenge + 4, 34))
373     {
374       closesocket(conn);
375       free(*buf);
376       return 0;
377     }
378
379   /* good enough */
380   free(*buf);
381
382   if (send(conn, response, sizeof(response), 0) != sizeof(response))
383     {
384       closesocket(conn);
385       return 0;
386     }
387   return conn;
388 }
This page took 0.076039 seconds and 5 git commands to generate.