Fixes excessive closes in accept()
[akaros.git] / user / bsd / socket.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9 /* posix */
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <errno.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18
19 /* bsd extensions */
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23
24 #include "priv.h"
25
26 Rock *_sock_rock;
27
28 Rock*
29 _sock_findrock(int fd, struct stat *dp)
30 {
31         Rock *r;
32         struct stat d;
33
34         if(dp == 0)
35                 dp = &d;
36         fstat(fd, dp);
37         for(r = _sock_rock; r; r = r->next){
38                 if(r->inode == dp->st_ino
39                 && r->dev == dp->st_dev)
40                         break;
41         }
42         return r;
43 }
44
45 Rock*
46 _sock_newrock(int fd)
47 {
48         Rock *r;
49         struct stat d;
50
51         r = _sock_findrock(fd, &d);
52         if(r == 0){
53                 r = malloc(sizeof(Rock));
54                 if(r == 0)
55                         return 0;
56                 r->dev = d.st_dev;
57                 r->inode = d.st_ino;
58                 r->other = -1;
59                 /* TODO: this is not thread-safe! */
60                 r->next = _sock_rock;
61                 _sock_rock = r;
62         }
63         memset(&r->raddr, 0, sizeof(r->raddr));
64         memset(&r->addr, 0, sizeof(r->addr));
65         r->reserved = 0;
66         r->dev = d.st_dev;
67         r->inode = d.st_ino;
68         r->other = -1;
69         return r;
70 }
71
72 /* For a ctlfd and a few other settings, it opens and returns the corresponding
73  * datafd.  This will close cfd for you. */
74 int
75 _sock_data(int cfd, char *net, int domain, int stype, int protocol, Rock **rp)
76 {
77         int n, fd;
78         Rock *r;
79         char name[Ctlsize];
80
81         /* get the data file name */
82         n = read(cfd, name, sizeof(name)-1);
83         if(n < 0){
84                 close(cfd);
85                 errno = ENOBUFS;
86                 return -1;
87         }
88         name[n] = 0;
89         n = strtoul(name, 0, 0);
90         snprintf(name, sizeof name, "/net/%s/%d/data", net, n);
91
92         /* open data file */
93         fd = open(name, O_RDWR);
94         close(cfd); /* close this no matter what */
95         if(fd < 0){
96                 errno = ENOBUFS;
97                 return -1;
98         }
99
100         /* hide stuff under the rock */
101         snprintf(name, sizeof name, "/net/%s/%d/ctl", net, n);
102         r = _sock_newrock(fd);
103         if(r == 0){
104                 errno = ENOBUFS;
105                 close(fd);
106                 return -1;
107         }
108         if(rp)
109                 *rp = r;
110         memset(&r->raddr, 0, sizeof(r->raddr));
111         memset(&r->addr, 0, sizeof(r->addr));
112         r->domain = domain;
113         r->stype = stype;
114         r->protocol = protocol;
115         strcpy(r->ctl, name);
116         return fd;
117 }
118
119 int
120 socket(int domain, int stype, int protocol)
121 {
122         Rock *r;
123         int cfd, fd, n;
124         int pfd[2];
125         char *net;
126         char msg[128];
127
128         switch(domain){
129         case PF_INET:
130                 /* get a free network directory */
131                 switch(stype){
132                 case SOCK_DGRAM:
133                         net = "udp";
134                         cfd = open("/net/udp/clone", O_RDWR);
135                         /* All BSD UDP sockets are in 'headers' mode, where each packet has
136                          * the remote addr:port, local addr:port and other info. */
137                         if (!(cfd < 0)) {
138                                 n = snprintf(msg, sizeof(msg), "headers");
139                                 n = write(cfd, msg, n);
140                                 if (n < 0) {
141                                         perror("UDP socket headers failed");
142                                         return -1;
143                                 }
144                         }
145                         break;
146                 case SOCK_STREAM:
147                         net = "tcp";
148                         cfd = open("/net/tcp/clone", O_RDWR);
149                         break;
150                 default:
151                         errno = EPROTONOSUPPORT;
152                         return -1;
153                 }
154                 if(cfd < 0){
155                         return -1;
156                 }
157                 return _sock_data(cfd, net, domain, stype, protocol, 0);
158         case PF_UNIX:
159                 if(pipe(pfd) < 0){
160                         return -1;
161                 }
162                 r = _sock_newrock(pfd[0]);
163                 r->domain = domain;
164                 r->stype = stype;
165                 r->protocol = protocol;
166                 r->other = pfd[1];
167                 return pfd[0];
168         default:
169                 errno = EPROTONOSUPPORT;
170                 return -1;
171         }
172 }
173
174 int
175 issocket(int fd)
176 {
177         Rock *r;
178
179         r = _sock_findrock(fd, 0);
180         return (r != 0);
181 }
182
183 /*
184  * probably should do better than this
185  */
186 int getsockopt (int __fd, int __level, int __optname,
187                        void *__restrict __optval,
188                        socklen_t *__restrict __optlen)
189 {
190         return -1;
191 }
192
193 int setsockopt (int __fd, int __level, int __optname,
194                        __const void *__optval, socklen_t __optlen)
195 {
196         return 0;
197 }
198