xref: /inferno-os/emu/port/ipif-posix.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 #ifdef sun
2 #define	uint uxuint
3 #define	ulong uxulong
4 #define	ushort uxushort
5 #endif
6 #include <sys/types.h>
7 #include	<sys/time.h>
8 #include	<sys/socket.h>
9 #include	<net/if.h>
10 #include	<net/if_arp.h>
11 #include	<netinet/in.h>
12 #include	<netinet/tcp.h>
13 #include	<arpa/inet.h>
14 #include	<netdb.h>
15 #include	<sys/ioctl.h>
16 #undef ulong
17 #undef ushort
18 #undef uint
19 
20 #include        "dat.h"
21 #include        "fns.h"
22 #include        "ip.h"
23 #include        "error.h"
24 
25 char Enotv4[] = "address not IPv4";
26 
27 static void
28 ipw6(uchar *a, ulong w)
29 {
30 	memmove(a, v4prefix, IPv4off);
31 	memmove(a+IPv4off, &w, IPv4addrlen);
32 }
33 
34 int
35 so_socket(int type)
36 {
37 	int fd, one;
38 
39 	switch(type) {
40 	default:
41 		error("bad protocol type");
42 	case S_TCP:
43 		type = SOCK_STREAM;
44 		break;
45 	case S_UDP:
46 		type = SOCK_DGRAM;
47 		break;
48 	}
49 
50 	fd = socket(AF_INET, type, 0);
51 	if(fd < 0)
52 		oserror();
53 	if(type == SOCK_DGRAM){
54 		one = 1;
55 		setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof(one));
56 	}else{
57 		one = 1;
58 		setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char*)&one, sizeof(one));
59 	}
60 	return fd;
61 }
62 
63 int
64 so_send(int sock, void *va, int len, void *hdr, int hdrlen)
65 {
66 	int r;
67 	struct sockaddr sa;
68 	struct sockaddr_in *sin;
69 	uchar *h = hdr;
70 
71 	osenter();
72 	if(hdr == 0)
73 		r = write(sock, va, len);
74 	else {
75 		memset(&sa, 0, sizeof(sa));
76 		sin = (struct sockaddr_in*)&sa;
77 		sin->sin_family = AF_INET;
78 		switch(hdrlen){
79 		case OUdphdrlenv4:
80 			memmove(&sin->sin_addr, h,  4);
81 			memmove(&sin->sin_port, h+8, 2);
82 			break;
83 		case OUdphdrlen:
84 			v6tov4((uchar*)&sin->sin_addr, h);
85 			memmove(&sin->sin_port, h+2*IPaddrlen, 2);	/* rport */
86 			break;
87 		default:
88 			v6tov4((uchar*)&sin->sin_addr, h);
89 			memmove(&sin->sin_port, h+3*IPaddrlen, 2);
90 			break;
91 		}
92 		r = sendto(sock, va, len, 0, &sa, sizeof(sa));
93 	}
94 	osleave();
95 	return r;
96 }
97 
98 int
99 so_recv(int sock, void *va, int len, void *hdr, int hdrlen)
100 {
101 	int r;
102 	socklen_t l;
103 	struct sockaddr sa;
104 	struct sockaddr_in *sin;
105 	uchar h[Udphdrlen];
106 
107 	osenter();
108 	if(hdr == 0)
109 		r = read(sock, va, len);
110 	else {
111 		sin = (struct sockaddr_in*)&sa;
112 		l = sizeof(sa);
113 		r = recvfrom(sock, va, len, 0, &sa, &l);
114 		if(r >= 0) {
115 			memset(h, 0, sizeof(h));
116 			switch(hdrlen){
117 			case OUdphdrlenv4:
118 				memmove(h, &sin->sin_addr, IPv4addrlen);
119 				memmove(h+2*IPv4addrlen, &sin->sin_port, 2);
120 				break;
121 			case OUdphdrlen:
122 				v4tov6(h, (uchar*)&sin->sin_addr);
123 				memmove(h+2*IPaddrlen, &sin->sin_port, 2);
124 				break;
125 			default:
126 				v4tov6(h, (uchar*)&sin->sin_addr);
127 				memmove(h+3*IPaddrlen, &sin->sin_port, 2);
128 				break;
129 			}
130 
131 			/* alas there's no way to get the local addr/port correctly.  Pretend. */
132 			memset(&sa, 0, sizeof(sa));
133 			getsockname(sock, &sa, &l);
134 			switch(hdrlen){
135 			case OUdphdrlenv4:
136 				memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen);
137 				memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2);
138 				break;
139 			case OUdphdrlen:
140 				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
141 				memmove(h+2*IPaddrlen+2, &sin->sin_port, 2);
142 				break;
143 			default:
144 				v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr);
145 				v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr);	/* ifcaddr */
146 				memmove(h+3*IPaddrlen+2, &sin->sin_port, 2);
147 				break;
148 			}
149 			memmove(hdr, h, hdrlen);
150 		}
151 	}
152 	osleave();
153 	return r;
154 }
155 
156 void
157 so_close(int sock)
158 {
159 	close(sock);
160 }
161 
162 void
163 so_connect(int fd, uchar *raddr, ushort rport)
164 {
165 	int r;
166 	struct sockaddr sa;
167 	struct sockaddr_in *sin;
168 
169 	if(!isv4(raddr))
170 		error(Enotv4);
171 
172 	memset(&sa, 0, sizeof(sa));
173 	sin = (struct sockaddr_in*)&sa;
174 	sin->sin_family = AF_INET;
175 	hnputs(&sin->sin_port, rport);
176 	memmove(&sin->sin_addr.s_addr, raddr+IPv4off, IPv4addrlen);
177 
178 	osenter();
179 	r = connect(fd, &sa, sizeof(sa));
180 	osleave();
181 	if(r < 0)
182 		oserror();
183 }
184 
185 void
186 so_getsockname(int fd, uchar *laddr, ushort *lport)
187 {
188 	socklen_t len;
189 	struct sockaddr sa;
190 	struct sockaddr_in *sin;
191 
192 	len = sizeof(sa);
193 	if(getsockname(fd, &sa, &len) < 0)
194 		oserror();
195 
196 	sin = (struct sockaddr_in*)&sa;
197 	if(sin->sin_family != AF_INET || len != sizeof(*sin))
198 		error(Enotv4);
199 
200 	ipw6(laddr, sin->sin_addr.s_addr);
201 	*lport = nhgets(&sin->sin_port);
202 }
203 
204 void
205 so_listen(int fd)
206 {
207 	int r;
208 
209 	osenter();
210 	r = listen(fd, 256);
211 	osleave();
212 	if(r < 0)
213 		oserror();
214 }
215 
216 int
217 so_accept(int fd, uchar *raddr, ushort *rport)
218 {
219 	int nfd;
220 	socklen_t len;
221 	struct sockaddr sa;
222 	struct sockaddr_in *sin;
223 
224 	sin = (struct sockaddr_in*)&sa;
225 
226 	len = sizeof(sa);
227 	osenter();
228 	nfd = accept(fd, &sa, &len);
229 	osleave();
230 	if(nfd < 0)
231 		oserror();
232 
233 	if(sin->sin_family != AF_INET || len != sizeof(*sin))
234 		error(Enotv4);
235 
236 	ipw6(raddr, sin->sin_addr.s_addr);
237 	*rport = nhgets(&sin->sin_port);
238 	return nfd;
239 }
240 
241 void
242 so_bind(int fd, int su, uchar *addr, ushort port)
243 {
244 	int i, one;
245 	struct sockaddr sa;
246 	struct sockaddr_in *sin;
247 
248 	sin = (struct sockaddr_in*)&sa;
249 
250 	one = 1;
251 	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) {
252 		oserrstr(up->genbuf, sizeof(up->genbuf));
253 		print("setsockopt: %s", up->genbuf);
254 	}
255 
256 	if(su) {
257 		for(i = 600; i < 1024; i++) {
258 			memset(&sa, 0, sizeof(sa));
259 			sin->sin_family = AF_INET;
260 			memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
261 			hnputs(&sin->sin_port, i);
262 
263 			if(bind(fd, &sa, sizeof(sa)) >= 0)
264 				return;
265 		}
266 		oserror();
267 	}
268 
269 	memset(&sa, 0, sizeof(sa));
270 	sin->sin_family = AF_INET;
271 	memmove(&sin->sin_addr.s_addr, addr+IPv4off, IPv4addrlen);
272 	hnputs(&sin->sin_port, port);
273 
274 	if(bind(fd, &sa, sizeof(sa)) < 0)
275 		oserror();
276 }
277 
278 int
279 so_gethostbyname(char *host, char**hostv, int n)
280 {
281 	int i;
282 	char buf[32];
283 	uchar *p;
284 	struct hostent *hp;
285 
286 	hp = gethostbyname(host);
287 	if(hp == 0)
288 		return 0;
289 
290 	for(i = 0; hp->h_addr_list[i] && i < n; i++) {
291 		p = (uchar*)hp->h_addr_list[i];
292 		sprint(buf, "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]);
293 		hostv[i] = strdup(buf);
294 		if(hostv[i] == 0)
295 			break;
296 	}
297 	return i;
298 }
299 
300 int
301 so_gethostbyaddr(char *addr, char **hostv, int n)
302 {
303 	int i;
304 	struct hostent *hp;
305 	unsigned long straddr;
306 
307 	straddr = inet_addr(addr);
308 	if(straddr == -1)
309 		return 0;
310 
311 	hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET);
312 	if(hp == 0)
313 		return 0;
314 
315 	hostv[0] = strdup(hp->h_name);
316 	if(hostv[0] == 0)
317 		return 0;
318 	for(i = 1; hp->h_aliases[i-1] && i < n; i++) {
319 		hostv[i] = strdup(hp->h_aliases[i-1]);
320 		if(hostv[i] == 0)
321 			break;
322 	}
323 	return i;
324 }
325 
326 int
327 so_getservbyname(char *service, char *net, char *port)
328 {
329 	ushort p;
330 	struct servent *s;
331 
332 	s = getservbyname(service, net);
333 	if(s == 0)
334 		return -1;
335 	p = s->s_port;
336 	sprint(port, "%d", nhgets(&p));
337 	return 0;
338 }
339 
340 int
341 so_hangup(int fd, int nolinger)
342 {
343 	int r;
344 	static struct linger l = {1, 0};
345 
346 	osenter();
347 	if(nolinger)
348 		setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l));
349 	r = shutdown(fd, 2);
350 	if(r >= 0)
351 		r = close(fd);
352 	osleave();
353 	return r;
354 }
355 
356 void
357 arpadd(char *ipaddr, char *eaddr, int n)
358 {
359 #ifdef SIOCGARP
360 	struct arpreq a;
361 	struct sockaddr_in pa;
362 	int s;
363 	uchar addr[IPaddrlen];
364 
365 	s = socket(AF_INET, SOCK_DGRAM, 0);
366 	memset(&a, 0, sizeof(a));
367 	memset(&pa, 0, sizeof(pa));
368 	pa.sin_family = AF_INET;
369 	pa.sin_port = 0;
370 	parseip(addr, ipaddr);
371 	if(!isv4(addr)){
372 		close(s);
373 		error(Ebadarg);
374 	}
375 	memmove(&pa.sin_addr, ipaddr+IPv4off, IPv4addrlen);
376 	memmove(&a.arp_pa, &pa, sizeof(pa));
377 	while(ioctl(s, SIOCGARP, &a) != -1) {
378 		ioctl(s, SIOCDARP, &a);
379 		memset(&a.arp_ha, 0, sizeof(a.arp_ha));
380 	}
381 	a.arp_ha.sa_family = AF_UNSPEC;
382 	parsemac((uchar*)a.arp_ha.sa_data, eaddr, 6);
383 	a.arp_flags = ATF_PERM;
384 	if(ioctl(s, SIOCSARP, &a) == -1) {
385 		oserrstr(up->env->errstr, ERRMAX);
386 		close(s);
387 		error(up->env->errstr);
388 	}
389 	close(s);
390 #else
391 	error("arp not implemented");
392 #endif
393 }
394 
395 int
396 so_mustbind(int restricted, int port)
397 {
398 	return restricted || port != 0;
399 }
400 
401 void
402 so_keepalive(int fd, int ms)
403 {
404 	int on;
405 
406 	on = 1;
407 	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on));
408 #ifdef TCP_KEEPIDLE
409 	if(ms <= 120000)
410 		ms = 120000;
411 	ms /= 1000;
412 	setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (char*)&ms, sizeof(ms));
413 #endif
414 }
415