Fixes gross stack consumers
[akaros.git] / kern / src / net / dial.c
1 // INFERNO
2 #include <vfs.h>
3 #include <kfs.h>
4 #include <slab.h>
5 #include <kmalloc.h>
6 #include <kref.h>
7 #include <string.h>
8 #include <stdio.h>
9 #include <assert.h>
10 #include <error.h>
11 #include <cpio.h>
12 #include <pmap.h>
13 #include <smp.h>
14 #include <ip.h>
15
16 typedef struct DS DS;
17
18 static int call(char *cp, char *cp1, DS * DS);
19 static int csdial(DS * DS);
20 static void _dial_string_parse(char *cp, DS * DS);
21 static int nettrans(char *cp, char *cp1, int na, char *cp2, int i);
22
23 enum {
24         Maxstring = 128,
25 };
26
27 struct DS {
28         char buf[Maxstring];            /* dist string */
29         char *netdir;
30         char *proto;
31         char *rem;
32         char *local;                            /* other args */
33         char *dir;
34         int *cfdp;
35 };
36
37 /* only used here for now. */
38 static void kerrstr(void *err, int len)
39 {
40         strncpy(err, current_errstr(), len);
41 }
42
43 /*
44  *  the dialstring is of the form '[/net/]proto!dest'
45  */
46 int kdial(char *dest, char *local, char *dir, int *cfdp)
47 {
48         DS ds;
49         int rv;
50         char *err, *alterr;
51
52         err = kmalloc(ERRMAX, KMALLOC_WAIT);
53         alterr = kmalloc(ERRMAX, KMALLOC_WAIT);
54
55         ds.local = local;
56         ds.dir = dir;
57         ds.cfdp = cfdp;
58
59         _dial_string_parse(dest, &ds);
60         if (ds.netdir) {
61                 rv = csdial(&ds);
62                 goto out;
63         }
64
65         ds.netdir = "/net";
66         rv = csdial(&ds);
67         if (rv >= 0)
68                 goto out;
69
70         err[0] = 0;
71         strncpy(err, current_errstr(), ERRMAX);
72         if (strstr(err, "refused") != 0) {
73                 goto out;
74         }
75
76         ds.netdir = "/net.alt";
77         rv = csdial(&ds);
78         if (rv >= 0)
79                 goto out;
80
81         alterr[0] = 0;
82         kerrstr(alterr, ERRMAX);
83
84         if (strstr(alterr, "translate") || strstr(alterr, "does not exist"))
85                 kerrstr(err, ERRMAX);
86         else
87                 kerrstr(alterr, ERRMAX);
88 out:
89         kfree(err);
90         kfree(alterr);
91         return rv;
92 }
93
94 static int csdial(DS * ds)
95 {
96         int n, fd, rv = -1;
97         char *p, *buf, *clone, *err, *besterr;
98
99         buf = kmalloc(Maxstring, KMALLOC_WAIT);
100         clone = kmalloc(Maxpath, KMALLOC_WAIT);
101         err = kmalloc(ERRMAX, KMALLOC_WAIT);
102         besterr = kmalloc(ERRMAX, KMALLOC_WAIT);
103         /*
104          *  open connection server
105          */
106         snprintf(buf, Maxstring, "%s/cs", ds->netdir);
107         fd = sysopen(buf, ORDWR);
108         if (fd < 0) {
109                 /* no connection server, don't translate */
110                 snprintf(clone, Maxpath, "%s/%s/clone", ds->netdir, ds->proto);
111                 rv = call(clone, ds->rem, ds);
112                 goto out;
113         }
114
115         /*
116          *  ask connection server to translate
117          */
118         snprintf(buf, Maxstring, "%s!%s", ds->proto, ds->rem);
119         if (syswrite(fd, buf, strlen(buf)) < 0) {
120                 kerrstr(err, ERRMAX);
121                 sysclose(fd);
122                 set_errstr("%s (%s)", err, buf);
123                 goto out;
124         }
125
126         /*
127          *  loop through each address from the connection server till
128          *  we get one that works.
129          */
130         *besterr = 0;
131         strncpy(err, Egreg, ERRMAX);
132         sysseek(fd, 0, 0);
133         while ((n = sysread(fd, buf, Maxstring - 1)) > 0) {
134                 buf[n] = 0;
135                 p = strchr(buf, ' ');
136                 if (p == 0)
137                         continue;
138                 *p++ = 0;
139                 rv = call(buf, p, ds);
140                 if (rv >= 0)
141                         break;
142                 err[0] = 0;
143                 kerrstr(err, ERRMAX);
144                 if (strstr(err, "does not exist") == 0)
145                         memmove(besterr, err, ERRMAX);
146         }
147         sysclose(fd);
148
149         if (rv < 0 && *besterr)
150                 kerrstr(besterr, ERRMAX);
151         else
152                 kerrstr(err, ERRMAX);
153 out:
154         kfree(buf);
155         kfree(clone);
156         kfree(err);
157         kfree(besterr);
158         return rv;
159 }
160
161 static int call(char *clone, char *dest, DS * ds)
162 {
163         int fd, cfd, n, retval;
164         char *name, *data, *err, *p;
165
166         name = kmalloc(Maxpath, KMALLOC_WAIT);
167         data = kmalloc(Maxpath, KMALLOC_WAIT);
168         err = kmalloc(ERRMAX, KMALLOC_WAIT);
169
170         cfd = sysopen(clone, ORDWR);
171         if (cfd < 0) {
172                 kerrstr(err, ERRMAX);
173                 set_errstr("%s (%s)", err, clone);
174                 retval = -1;
175                 goto out;
176         }
177
178         /* get directory name */
179         n = sysread(cfd, name, Maxpath - 1);
180         if (n < 0) {
181                 kerrstr(err, ERRMAX);
182                 sysclose(cfd);
183                 set_errstr("read %s: %s", clone, err);
184                 retval = -1;
185                 goto out;
186         }
187         name[n] = 0;
188         for (p = name; *p == ' '; p++) ;
189         snprintf(name, Maxpath, "%ld", strtoul(p, 0, 0));
190         p = strrchr(clone, '/');
191         *p = 0;
192         if (ds->dir)
193                 snprintf(ds->dir, NETPATHLEN, "%s/%s", clone, name);
194         snprintf(data, Maxpath, "%s/%s/data", clone, name);
195
196         /* connect */
197         if (ds->local)
198                 snprintf(name, Maxpath, "connect %s %s", dest, ds->local);
199         else
200                 snprintf(name, Maxpath, "connect %s", dest);
201         if (syswrite(cfd, name, strlen(name)) < 0) {
202                 err[0] = 0;
203                 kerrstr(err, ERRMAX);
204                 sysclose(cfd);
205                 set_errstr("%s (%s)", err, name);
206                 retval = -1;
207                 goto out;
208         }
209
210         /* open data connection */
211         fd = sysopen(data, ORDWR);
212         if (fd < 0) {
213                 err[0] = 0;
214                 kerrstr(err, ERRMAX);
215                 set_errstr("%s (%s)", err, data);
216                 sysclose(cfd);
217                 retval = -1;
218                 goto out;
219         }
220         if (ds->cfdp)
221                 *ds->cfdp = cfd;
222         else
223                 sysclose(cfd);
224         retval = fd;
225 out:
226         kfree(name);
227         kfree(data);
228         kfree(err);
229
230         return retval;
231 }
232
233 /*
234  *  parse a dial string
235  */
236 static void _dial_string_parse(char *str, DS * ds)
237 {
238         char *p, *p2;
239
240         strncpy(ds->buf, str, Maxstring);
241         ds->buf[Maxstring - 1] = 0;
242
243         p = strchr(ds->buf, '!');
244         if (p == 0) {
245                 ds->netdir = 0;
246                 ds->proto = "net";
247                 ds->rem = ds->buf;
248         } else {
249                 if (*ds->buf != '/' && *ds->buf != '#') {
250                         ds->netdir = 0;
251                         ds->proto = ds->buf;
252                 } else {
253                         for (p2 = p; *p2 != '/'; p2--) ;
254                         *p2++ = 0;
255                         ds->netdir = ds->buf;
256                         ds->proto = p2;
257                 }
258                 *p = 0;
259                 ds->rem = p + 1;
260         }
261 }
262
263 /*
264  *  announce a network service.
265  */
266 int kannounce(char *addr, char *dir)
267 {
268         int ctl, n, m;
269         char buf[NETPATHLEN];
270         char buf2[Maxpath];
271         char netdir[NETPATHLEN];
272         char naddr[Maxpath];
273         char *cp;
274
275         /*
276          *  translate the address
277          */
278         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
279                 return -1;
280
281         /*
282          * get a control channel
283          */
284         ctl = sysopen(netdir, ORDWR);
285         if (ctl < 0)
286                 return -1;
287         cp = strrchr(netdir, '/');
288         *cp = 0;
289
290         /*
291          *  find out which line we have
292          */
293         n = snprintf(buf, sizeof(buf), "%.*s/", sizeof buf, netdir);
294         m = sysread(ctl, &buf[n], sizeof(buf) - n - 1);
295         if (m <= 0) {
296                 sysclose(ctl);
297                 return -1;
298         }
299         buf[n + m] = 0;
300
301         /*
302          *  make the call
303          */
304         n = snprintf(buf2, sizeof buf2, "announce %s", naddr);
305         if (syswrite(ctl, buf2, n) != n) {
306                 sysclose(ctl);
307                 return -1;
308         }
309
310         /*
311          *  return directory etc.
312          */
313         if (dir)
314                 strncpy(dir, buf, sizeof(dir));
315         return ctl;
316 }
317
318 /*
319  *  listen for an incoming call
320  */
321 int klisten(char *dir, char *newdir)
322 {
323         int ctl, n, m;
324         char buf[NETPATHLEN];
325         char *cp;
326
327         /*
328          *  open listen, wait for a call
329          */
330         snprintf(buf, sizeof buf, "%s/listen", dir);
331         ctl = sysopen(buf, ORDWR);
332         if (ctl < 0)
333                 return -1;
334
335         /*
336          *  find out which line we have
337          */
338         strncpy(buf, dir, sizeof(buf));
339         cp = strrchr(buf, '/');
340         *++cp = 0;
341         n = cp - buf;
342         m = sysread(ctl, cp, sizeof(buf) - n - 1);
343         if (m <= 0) {
344                 sysclose(ctl);
345                 return -1;
346         }
347         buf[n + m] = 0;
348
349         /*
350          *  return directory etc.
351          */
352         if (newdir)
353                 strncpy(newdir, buf, sizeof(newdir));
354         return ctl;
355
356 }
357
358 /*
359  *  perform the identity translation (in case we can't reach cs)
360  */
361 static int
362 identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
363 {
364         char proto[Maxpath];
365         char *p;
366
367         /* parse the protocol */
368         strncpy(proto, addr, sizeof(proto));
369         proto[sizeof(proto) - 1] = 0;
370         p = strchr(proto, '!');
371         if (p)
372                 *p++ = 0;
373
374         snprintf(file, nf, "%s/%s/clone", netdir, proto);
375         strncpy(naddr, p, na);
376         naddr[na - 1] = 0;
377
378         return 1;
379 }
380
381 /*
382  *  call up the connection server and get a translation
383  */
384 static int nettrans(char *addr, char *naddr, int na, char *file, int nf)
385 {
386         int i, fd;
387         char buf[Maxpath];
388         char netdir[NETPATHLEN];
389         char *p, *p2;
390         long n;
391
392         /*
393          *  parse, get network directory
394          */
395         p = strchr(addr, '!');
396         if (p == 0) {
397                 set_errstr("bad dial string: %s", addr);
398                 return -1;
399         }
400         if (*addr != '/') {
401                 strncpy(netdir, "/net", sizeof(netdir));
402         } else {
403                 for (p2 = p; *p2 != '/'; p2--) ;
404                 i = p2 - addr;
405                 if (i == 0 || i >= sizeof(netdir)) {
406                         set_errstr("bad dial string: %s", addr);
407                         return -1;
408                 }
409                 strncpy(netdir, addr, i);
410                 netdir[i] = 0;
411                 addr = p2 + 1;
412         }
413
414         /*
415          *  ask the connection server
416          */
417         snprintf(buf, sizeof(buf), "%s/cs", netdir);
418         fd = sysopen(buf, ORDWR);
419         if (fd < 0)
420                 return identtrans(netdir, addr, naddr, na, file, nf);
421         if (syswrite(fd, addr, strlen(addr)) < 0) {
422                 sysclose(fd);
423                 return -1;
424         }
425         sysseek(fd, 0, 0);
426         n = sysread(fd, buf, sizeof(buf) - 1);
427         sysclose(fd);
428         if (n <= 0)
429                 return -1;
430         buf[n] = 0;
431
432         /*
433          *  parse the reply
434          */
435         p = strchr(buf, ' ');
436         if (p == 0)
437                 return -1;
438         *p++ = 0;
439         strncpy(naddr, p, na);
440         naddr[na - 1] = 0;
441         strncpy(file, buf, nf);
442         file[nf - 1] = 0;
443         return 0;
444 }