4c6be09949be3a36d5ad0811b2fbf35db00a8347
[akaros.git] / kern / src / net / dial.c
1 /* Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
2  * Portions Copyright © 1997-1999 Vita Nuova Limited
3  * Portions Copyright © 2000-2007 Vita Nuova Holdings Limited
4  *                                (www.vitanuova.com)
5  * Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
6  *
7  * Modified for the Akaros operating system:
8  * Copyright (c) 2013-2014 The Regents of the University of California
9  * Copyright (c) 2013-2015 Google Inc.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a copy
12  * of this software and associated documentation files (the "Software"), to deal
13  * in the Software without restriction, including without limitation the rights
14  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  * copies of the Software, and to permit persons to whom the Software is
16  * furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
24  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  * SOFTWARE. */
28
29 #include <vfs.h>
30 #include <kfs.h>
31 #include <slab.h>
32 #include <kmalloc.h>
33 #include <kref.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <assert.h>
37 #include <error.h>
38 #include <cpio.h>
39 #include <pmap.h>
40 #include <smp.h>
41 #include <ip.h>
42
43 typedef struct DS DS;
44
45 static int call(char *cp, char *cp1, DS * DS);
46 static int csdial(DS * DS);
47 static void _dial_string_parse(char *cp, DS * DS);
48 static int nettrans(char *cp, char *cp1, int na, char *cp2, int i);
49
50 enum {
51         Maxstring = 128,
52 };
53
54 struct DS {
55         char buf[Maxstring];            /* dist string */
56         char *netdir;
57         char *proto;
58         char *rem;
59         char *local;                            /* other args */
60         char *dir;
61         int *cfdp;
62 };
63
64 /* only used here for now. */
65 static void kerrstr(void *err, int len)
66 {
67         strncpy(err, current_errstr(), len);
68 }
69
70 /*
71  *  the dialstring is of the form '[/net/]proto!dest'
72  */
73 int kdial(char *dest, char *local, char *dir, int *cfdp)
74 {
75         DS ds;
76         int rv;
77         char *err, *alterr;
78
79         err = kmalloc(ERRMAX, KMALLOC_WAIT);
80         alterr = kmalloc(ERRMAX, KMALLOC_WAIT);
81
82         ds.local = local;
83         ds.dir = dir;
84         ds.cfdp = cfdp;
85
86         _dial_string_parse(dest, &ds);
87         if (ds.netdir) {
88                 rv = csdial(&ds);
89                 goto out;
90         }
91
92         ds.netdir = "/net";
93         rv = csdial(&ds);
94         if (rv >= 0)
95                 goto out;
96
97         err[0] = 0;
98         strncpy(err, current_errstr(), ERRMAX);
99         if (strstr(err, "refused") != 0) {
100                 goto out;
101         }
102
103         ds.netdir = "/net.alt";
104         rv = csdial(&ds);
105         if (rv >= 0)
106                 goto out;
107
108         alterr[0] = 0;
109         kerrstr(alterr, ERRMAX);
110
111         if (strstr(alterr, "translate") || strstr(alterr, "does not exist"))
112                 kerrstr(err, ERRMAX);
113         else
114                 kerrstr(alterr, ERRMAX);
115 out:
116         kfree(err);
117         kfree(alterr);
118         return rv;
119 }
120
121 static int csdial(DS * ds)
122 {
123         int n, fd, rv = -1;
124         char *p, *buf, *clone, *err, *besterr;
125
126         buf = kmalloc(Maxstring, KMALLOC_WAIT);
127         clone = kmalloc(Maxpath, KMALLOC_WAIT);
128         err = kmalloc(ERRMAX, KMALLOC_WAIT);
129         besterr = kmalloc(ERRMAX, KMALLOC_WAIT);
130         /*
131          *  open connection server
132          */
133         snprintf(buf, Maxstring, "%s/cs", ds->netdir);
134         fd = sysopen(buf, O_RDWR);
135         if (fd < 0) {
136                 /* no connection server, don't translate */
137                 snprintf(clone, Maxpath, "%s/%s/clone", ds->netdir, ds->proto);
138                 rv = call(clone, ds->rem, ds);
139                 goto out;
140         }
141
142         /*
143          *  ask connection server to translate
144          */
145         snprintf(buf, Maxstring, "%s!%s", ds->proto, ds->rem);
146         if (syswrite(fd, buf, strlen(buf)) < 0) {
147                 kerrstr(err, ERRMAX);
148                 sysclose(fd);
149                 set_errstr("%s (%s)", err, buf);
150                 goto out;
151         }
152
153         /*
154          *  loop through each address from the connection server till
155          *  we get one that works.
156          */
157         *besterr = 0;
158         strncpy(err, errno_to_string(ECONNRESET), ERRMAX);
159         sysseek(fd, 0, 0);
160         while ((n = sysread(fd, buf, Maxstring - 1)) > 0) {
161                 buf[n] = 0;
162                 p = strchr(buf, ' ');
163                 if (p == 0)
164                         continue;
165                 *p++ = 0;
166                 rv = call(buf, p, ds);
167                 if (rv >= 0)
168                         break;
169                 err[0] = 0;
170                 kerrstr(err, ERRMAX);
171                 if (strstr(err, "does not exist") == 0)
172                         memmove(besterr, err, ERRMAX);
173         }
174         sysclose(fd);
175
176         if (rv < 0 && *besterr)
177                 kerrstr(besterr, ERRMAX);
178         else
179                 kerrstr(err, ERRMAX);
180 out:
181         kfree(buf);
182         kfree(clone);
183         kfree(err);
184         kfree(besterr);
185         return rv;
186 }
187
188 static int call(char *clone, char *dest, DS * ds)
189 {
190         int fd, cfd, n, retval;
191         char *name, *data, *err, *p;
192
193         name = kmalloc(Maxpath, KMALLOC_WAIT);
194         data = kmalloc(Maxpath, KMALLOC_WAIT);
195         err = kmalloc(ERRMAX, KMALLOC_WAIT);
196
197         cfd = sysopen(clone, O_RDWR);
198         if (cfd < 0) {
199                 kerrstr(err, ERRMAX);
200                 set_errstr("%s (%s)", err, clone);
201                 retval = -1;
202                 goto out;
203         }
204
205         /* get directory name */
206         n = sysread(cfd, name, Maxpath - 1);
207         if (n < 0) {
208                 kerrstr(err, ERRMAX);
209                 sysclose(cfd);
210                 set_errstr("read %s: %s", clone, err);
211                 retval = -1;
212                 goto out;
213         }
214         name[n] = 0;
215         for (p = name; *p == ' '; p++) ;
216         snprintf(name, Maxpath, "%ld", strtoul(p, 0, 0));
217         p = strrchr(clone, '/');
218         *p = 0;
219         if (ds->dir)
220                 snprintf(ds->dir, NETPATHLEN, "%s/%s", clone, name);
221         snprintf(data, Maxpath, "%s/%s/data", clone, name);
222
223         /* connect */
224         if (ds->local)
225                 snprintf(name, Maxpath, "connect %s %s", dest, ds->local);
226         else
227                 snprintf(name, Maxpath, "connect %s", dest);
228         if (syswrite(cfd, name, strlen(name)) < 0) {
229                 err[0] = 0;
230                 kerrstr(err, ERRMAX);
231                 sysclose(cfd);
232                 set_errstr("%s (%s)", err, name);
233                 retval = -1;
234                 goto out;
235         }
236
237         /* open data connection */
238         fd = sysopen(data, O_RDWR);
239         if (fd < 0) {
240                 err[0] = 0;
241                 kerrstr(err, ERRMAX);
242                 set_errstr("%s (%s)", err, data);
243                 sysclose(cfd);
244                 retval = -1;
245                 goto out;
246         }
247         if (ds->cfdp)
248                 *ds->cfdp = cfd;
249         else
250                 sysclose(cfd);
251         retval = fd;
252 out:
253         kfree(name);
254         kfree(data);
255         kfree(err);
256
257         return retval;
258 }
259
260 /*
261  *  parse a dial string
262  */
263 static void _dial_string_parse(char *str, DS * ds)
264 {
265         char *p, *p2;
266
267         strncpy(ds->buf, str, Maxstring);
268         ds->buf[Maxstring - 1] = 0;
269
270         p = strchr(ds->buf, '!');
271         if (p == 0) {
272                 ds->netdir = 0;
273                 ds->proto = "net";
274                 ds->rem = ds->buf;
275         } else {
276                 if (*ds->buf != '/' && *ds->buf != '#') {
277                         ds->netdir = 0;
278                         ds->proto = ds->buf;
279                 } else {
280                         for (p2 = p; *p2 != '/'; p2--) ;
281                         *p2++ = 0;
282                         ds->netdir = ds->buf;
283                         ds->proto = p2;
284                 }
285                 *p = 0;
286                 ds->rem = p + 1;
287         }
288 }
289
290 /*
291  *  announce a network service.
292  */
293 int kannounce(char *addr, char *dir)
294 {
295         int ctl, n, m;
296         char buf[NETPATHLEN];
297         char buf2[Maxpath];
298         char netdir[NETPATHLEN];
299         char naddr[Maxpath];
300         char *cp;
301
302         /*
303          *  translate the address
304          */
305         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
306                 return -1;
307
308         /*
309          * get a control channel
310          */
311         ctl = sysopen(netdir, O_RDWR);
312         if (ctl < 0)
313                 return -1;
314         cp = strrchr(netdir, '/');
315         *cp = 0;
316
317         /*
318          *  find out which line we have
319          */
320         n = snprintf(buf, sizeof(buf), "%.*s/", sizeof buf, netdir);
321         m = sysread(ctl, &buf[n], sizeof(buf) - n - 1);
322         if (m <= 0) {
323                 sysclose(ctl);
324                 return -1;
325         }
326         buf[n + m] = 0;
327
328         /*
329          *  make the call
330          */
331         n = snprintf(buf2, sizeof buf2, "announce %s", naddr);
332         if (syswrite(ctl, buf2, n) != n) {
333                 sysclose(ctl);
334                 return -1;
335         }
336
337         /*
338          *  return directory etc.
339          */
340         if (dir)
341                 strncpy(dir, buf, sizeof(dir));
342         return ctl;
343 }
344
345 /*
346  *  listen for an incoming call
347  */
348 int klisten(char *dir, char *newdir)
349 {
350         int ctl, n, m;
351         char buf[NETPATHLEN];
352         char *cp;
353
354         /*
355          *  open listen, wait for a call
356          */
357         snprintf(buf, sizeof buf, "%s/listen", dir);
358         ctl = sysopen(buf, O_RDWR);
359         if (ctl < 0)
360                 return -1;
361
362         /*
363          *  find out which line we have
364          */
365         strncpy(buf, dir, sizeof(buf));
366         cp = strrchr(buf, '/');
367         *++cp = 0;
368         n = cp - buf;
369         m = sysread(ctl, cp, sizeof(buf) - n - 1);
370         if (m <= 0) {
371                 sysclose(ctl);
372                 return -1;
373         }
374         buf[n + m] = 0;
375
376         /*
377          *  return directory etc.
378          */
379         if (newdir)
380                 strncpy(newdir, buf, sizeof(newdir));
381         return ctl;
382
383 }
384
385 /*
386  *  perform the identity translation (in case we can't reach cs)
387  */
388 static int
389 identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
390 {
391         char proto[Maxpath];
392         char *p;
393
394         /* parse the protocol */
395         strncpy(proto, addr, sizeof(proto));
396         proto[sizeof(proto) - 1] = 0;
397         p = strchr(proto, '!');
398         if (p)
399                 *p++ = 0;
400
401         snprintf(file, nf, "%s/%s/clone", netdir, proto);
402         strncpy(naddr, p, na);
403         naddr[na - 1] = 0;
404
405         return 1;
406 }
407
408 /*
409  *  call up the connection server and get a translation
410  */
411 static int nettrans(char *addr, char *naddr, int na, char *file, int nf)
412 {
413         int i, fd;
414         char buf[Maxpath];
415         char netdir[NETPATHLEN];
416         char *p, *p2;
417         long n;
418
419         /*
420          *  parse, get network directory
421          */
422         p = strchr(addr, '!');
423         if (p == 0) {
424                 set_errstr("bad dial string: %s", addr);
425                 return -1;
426         }
427         if (*addr != '/') {
428                 strncpy(netdir, "/net", sizeof(netdir));
429         } else {
430                 for (p2 = p; *p2 != '/'; p2--) ;
431                 i = p2 - addr;
432                 if (i == 0 || i >= sizeof(netdir)) {
433                         set_errstr("bad dial string: %s", addr);
434                         return -1;
435                 }
436                 strncpy(netdir, addr, i);
437                 netdir[i] = 0;
438                 addr = p2 + 1;
439         }
440
441         /*
442          *  ask the connection server
443          */
444         snprintf(buf, sizeof(buf), "%s/cs", netdir);
445         fd = sysopen(buf, O_RDWR);
446         if (fd < 0)
447                 return identtrans(netdir, addr, naddr, na, file, nf);
448         if (syswrite(fd, addr, strlen(addr)) < 0) {
449                 sysclose(fd);
450                 return -1;
451         }
452         sysseek(fd, 0, 0);
453         n = sysread(fd, buf, sizeof(buf) - 1);
454         sysclose(fd);
455         if (n <= 0)
456                 return -1;
457         buf[n] = 0;
458
459         /*
460          *  parse the reply
461          */
462         p = strchr(buf, ' ');
463         if (p == 0)
464                 return -1;
465         *p++ = 0;
466         strncpy(naddr, p, na);
467         naddr[na - 1] = 0;
468         strncpy(file, buf, nf);
469         file[nf - 1] = 0;
470         return 0;
471 }