qio: Track the amount of bytes read
[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         strlcpy(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, MEM_WAIT);
80         alterr = kmalloc(ERRMAX, MEM_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         strlcpy(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, MEM_WAIT);
127         clone = kmalloc(Maxpath, MEM_WAIT);
128         err = kmalloc(ERRMAX, MEM_WAIT);
129         besterr = kmalloc(ERRMAX, MEM_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         strlcpy(err, "csdial() connection reset", 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, MEM_WAIT);
194         data = kmalloc(Maxpath, MEM_WAIT);
195         err = kmalloc(ERRMAX, MEM_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         strlcpy(ds->buf, str, Maxstring);
268
269         p = strchr(ds->buf, '!');
270         if (p == 0) {
271                 ds->netdir = 0;
272                 ds->proto = "net";
273                 ds->rem = ds->buf;
274         } else {
275                 if (*ds->buf != '/' && *ds->buf != '#') {
276                         ds->netdir = 0;
277                         ds->proto = ds->buf;
278                 } else {
279                         for (p2 = p; *p2 != '/'; p2--) ;
280                         *p2++ = 0;
281                         ds->netdir = ds->buf;
282                         ds->proto = p2;
283                 }
284                 *p = 0;
285                 ds->rem = p + 1;
286         }
287 }
288
289 /*
290  *  announce a network service.
291  */
292 int kannounce(char *addr, char *dir, size_t dirlen)
293 {
294         int ctl, n, m;
295         char buf[NETPATHLEN];
296         char buf2[Maxpath];
297         char netdir[NETPATHLEN];
298         char naddr[Maxpath];
299         char *cp;
300
301         /*
302          *  translate the address
303          */
304         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
305                 return -1;
306
307         /*
308          * get a control channel
309          */
310         ctl = sysopen(netdir, O_RDWR);
311         if (ctl < 0)
312                 return -1;
313         cp = strrchr(netdir, '/');
314         *cp = 0;
315
316         /*
317          *  find out which line we have
318          */
319         n = snprintf(buf, sizeof(buf), "%.*s/", sizeof buf, netdir);
320         m = sysread(ctl, &buf[n], sizeof(buf) - n - 1);
321         if (m <= 0) {
322                 sysclose(ctl);
323                 return -1;
324         }
325         buf[n + m] = 0;
326
327         /*
328          *  make the call
329          */
330         n = snprintf(buf2, sizeof buf2, "announce %s", naddr);
331         if (syswrite(ctl, buf2, n) != n) {
332                 sysclose(ctl);
333                 return -1;
334         }
335
336         /*
337          *  return directory etc.
338          */
339         if (dir)
340                 strlcpy(dir, buf, dirlen);
341         return ctl;
342 }
343
344 /*
345  *  listen for an incoming call
346  */
347 int klisten(char *dir, char *newdir, size_t newdirlen)
348 {
349         int ctl, n, m;
350         char buf[NETPATHLEN + 1];
351         char *cp;
352
353         /*
354          *  open listen, wait for a call
355          */
356         snprintf(buf, sizeof buf, "%s/listen", dir);
357         ctl = sysopen(buf, O_RDWR);
358         if (ctl < 0)
359                 return -1;
360
361         /*
362          *  find out which line we have
363          */
364         strlcpy(buf, dir, sizeof(buf));
365         cp = strrchr(buf, '/');
366         *++cp = 0;
367         n = cp - buf;
368         m = sysread(ctl, cp, sizeof(buf) - n - 1);
369         if (m <= 0) {
370                 sysclose(ctl);
371                 return -1;
372         }
373         buf[n + m] = 0;
374
375         /*
376          *  return directory etc.
377          */
378         if (newdir)
379                 strlcpy(newdir, buf, newdirlen);
380         return ctl;
381
382 }
383
384 /*
385  *  perform the identity translation (in case we can't reach cs)
386  */
387 static int
388 identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
389 {
390         char proto[Maxpath];
391         char *p;
392
393         /* parse the protocol */
394         strlcpy(proto, addr, sizeof(proto));
395         p = strchr(proto, '!');
396         if (p)
397                 *p++ = 0;
398
399         snprintf(file, nf, "%s/%s/clone", netdir, proto);
400         strlcpy(naddr, p, na);
401
402         return 1;
403 }
404
405 /*
406  *  call up the connection server and get a translation
407  */
408 static int nettrans(char *addr, char *naddr, int na, char *file, int nf)
409 {
410         int i, fd;
411         char buf[Maxpath];
412         char netdir[NETPATHLEN];
413         char *p, *p2;
414         long n;
415
416         /*
417          *  parse, get network directory
418          */
419         p = strchr(addr, '!');
420         if (p == 0) {
421                 set_errstr("bad dial string: %s", addr);
422                 return -1;
423         }
424         if (*addr != '/') {
425                 strlcpy(netdir, "/net", sizeof(netdir));
426         } else {
427                 for (p2 = p; *p2 != '/'; p2--) ;
428                 i = p2 - addr;
429                 if (i == 0 || i >= sizeof(netdir)) {
430                         set_errstr("bad dial string: %s", addr);
431                         return -1;
432                 }
433                 strlcpy(netdir, addr, i + 1);
434                 addr = p2 + 1;
435         }
436
437         /*
438          *  ask the connection server
439          */
440         snprintf(buf, sizeof(buf), "%s/cs", netdir);
441         fd = sysopen(buf, O_RDWR);
442         if (fd < 0)
443                 return identtrans(netdir, addr, naddr, na, file, nf);
444         if (syswrite(fd, addr, strlen(addr)) < 0) {
445                 sysclose(fd);
446                 return -1;
447         }
448         sysseek(fd, 0, 0);
449         n = sysread(fd, buf, sizeof(buf) - 1);
450         sysclose(fd);
451         if (n <= 0)
452                 return -1;
453         buf[n] = 0;
454
455         /*
456          *  parse the reply
457          */
458         p = strchr(buf, ' ');
459         if (p == 0)
460                 return -1;
461         *p++ = 0;
462         strlcpy(naddr, p, na);
463         strlcpy(file, buf, nf);
464         return 0;
465 }