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