xref: /original-bsd/sys/deprecated/bbnnet/in_pcb.c (revision 8af5b582)
1 #ifdef	RCSIDENT
2 static char rcsident[] = "$Header: in_pcb.c,v 1.12 84/11/29 17:02:13 walsh Exp $";
3 #endif	RCSIDENT
4 
5 #include "../h/param.h"
6 #include "../h/systm.h"
7 #include "../h/dir.h"
8 #include "../h/user.h"
9 #include "../h/mbuf.h"
10 #include "../h/socket.h"
11 #include "../h/socketvar.h"
12 #include "../h/protosw.h"
13 #include "../h/domain.h"
14 
15 #include "../net/if.h"
16 #include "../net/route.h"
17 
18 #include "../bbnnet/in.h"
19 #include "../bbnnet/net.h"
20 #include "../bbnnet/in_pcb.h"
21 #include "../bbnnet/in_var.h"
22 
23 extern struct rtentry *ip_route();
24 extern struct domain  inetdomain;
25 
26 in_pcballoc(so, head)
27 struct socket *so;
28 struct inpcb *head;
29 {
30     register struct mbuf *m;
31     register struct inpcb *inp;
32 
33     m = m_getclr(M_DONTWAIT, MT_PCB);
34     if (m == NULL)
35 	return (ENOBUFS);
36 
37     inp = mtod(m, struct inpcb *);
38     inp->inp_socket = so;
39 
40     insque(inp,head);
41 
42     so->so_pcb = (caddr_t)inp;
43 
44     return (0);
45 }
46 
47 /*
48  * changed from 4.2 to accept a structure which has protocol
49  * specific data like how to break down port allocation.
50  */
51 
52 in_pcbbind(inp, nam, advice)
53 register struct inpcb *inp;
54 struct mbuf *nam;
55 struct pr_advice *advice;
56 {
57     register struct socket *so = inp->inp_socket;
58     register struct sockaddr_in *sin;
59     register u_short lport = 0;
60 
61     if (in_ifaddr == NULL)
62 	return (EADDRNOTAVAIL);
63     /*
64      * socket must not already be bound
65      */
66 
67     if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
68 	return (EINVAL);
69 
70     if (nam == 0)
71 	goto noname;
72     sin = mtod(nam, struct sockaddr_in *);
73     if (nam->m_len != sizeof (*sin))
74 	return (EINVAL);
75     /*
76      * Since Berkeley left this out, some of their programs (ftpd)
77      * aren't ready for it
78      *
79     if (sin->sin_family != AF_INET)
80 	    return (EAFNOSUPPORT);
81      */
82 
83     if (sin->sin_addr.s_addr != INADDR_ANY)
84     {
85 	/* some code says ..withnet() */
86 	if (in_iawithaddr(sin->sin_addr, FALSE) == NULL)
87 	    return (EADDRNOTAVAIL);
88 
89     }
90 
91     /* user gives port to us in net order */
92     if (lport = sin->sin_port)
93     {
94 	u_short aport;
95 
96 	/* if portsize > 2 a major rewrite needed to
97 	 * accomodate longs.....
98 	 */
99 
100 	if (advice->portsize > 1)
101 	    aport = ntohs(lport);
102 	else
103 	{
104 	    if (lport != (lport & 0xff))
105 		return(EINVAL);	/* must be 8 bits */
106 
107 	    aport = lport;	/* a char is a char */
108 	}
109 
110 	/*
111 	 *  really only a worry for byte size ports
112 	 */
113 
114 	if (aport > advice->maxport)
115 	    return(EADDRNOTAVAIL);
116 
117 	if (aport <= advice->rootport && u.u_uid != 0)
118 	    return (EACCES);
119 
120 	/*
121 	 * Check to see if the local address/port is in use.
122 	 * but, process may use this pair to communicate with
123 	 * several destinations (each with its own tcp) if he
124 	 * sets SO_REUSEADDR
125 	 */
126 	if (advice->bind_used &&
127 	    (*(advice->bind_used))(inp,		/* current binding */
128 	    lport,			/* desired port */
129 	    sin->sin_addr.s_addr,	/* desired address */
130 	    so->so_options & SO_REUSEADDR))
131 	{
132 	    return (EADDRINUSE);
133 	}
134     }
135     inp->inp_laddr = sin->sin_addr;
136 
137 noname :
138     /* any ports for random allocation by non-root users? */
139     if ((advice->maxport <= advice->resvport) && (u.u_uid))
140 	return(EADDRNOTAVAIL);
141 
142     if (lport == 0)
143     {
144 	/*
145 	 * Allow for reserved ports for non-super users
146 	 * so that don't interfere with some project's software.
147 	 */
148 	u_short possible = advice->nowport;
149 
150 	do
151 	{
152 	    if (advice->portsize > 1)
153 		lport = htons(possible);
154 	    else
155 		lport = possible;
156 
157 	    /*
158 	     * catch roll over.....
159 	     */
160 
161 	    if (possible >= advice->maxport)
162 		possible = advice->resvport + 1;
163 	    else
164 		possible++;
165 
166 	    /*
167 	     * no free ports??? RDP/HMP problem
168 	     */
169 
170 	    if (possible == advice->nowport)
171 		return(EADDRNOTAVAIL);
172 
173 	}
174 	while (advice->bind_used &&
175 	    (*(advice->bind_used))(inp, lport, inp->inp_laddr.s_addr, 0));
176 
177 	advice->nowport = possible;
178     }
179     inp->inp_lport = lport;
180     return (0);
181 }
182 
183 /*
184  * Connect from a socket to a specified address.
185  * Both address and port must be specified in argument sin.
186  * If don't have a local address for this socket yet,
187  * then pick one.
188  */
189 in_pcbconnect(inp, nam, conn_used)
190 struct inpcb *inp;
191 struct mbuf *nam;
192 char *(*conn_used)();
193 {
194     register struct ifnet *ifp = NULL;
195     register struct in_ifaddr *ia = NULL;
196     register struct sockaddr_in *ifaddr;
197     register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
198     register struct rtentry *rt;
199     struct sockaddr_in *inpsin;
200 
201     if (nam->m_len != sizeof (*sin))
202 	return (EINVAL);
203     if (sin->sin_family != AF_INET)
204 	return (EAFNOSUPPORT);
205     if (sin->sin_addr.s_addr == INADDR_ANY || sin->sin_port == 0)
206 	return (EADDRNOTAVAIL);
207 
208     /*
209      * Find route for connection.  For a tcp connecting to a server,
210      * this route will be used for the duration of the connection
211      * (unless redirected...).  For a UDP doing a connect, this route
212      * will also be used for the duration.  For a UDP unconnected send,
213      * this route will be used for the current packet.
214      *
215      * rtalloc cannot handle routing with both sides already bound
216      */
217     rt = (struct rtentry *) NULL;
218 
219     /*
220      * NOTE: programmers often forget to zero sin_zero[0-1].
221      * rtalloc does not want to know the port number for routes to hosts.
222      */
223     inpsin = (struct sockaddr_in *) &inp->inp_route.ro_dst;
224     bcopy((caddr_t)sin, (caddr_t)inpsin, sizeof (*sin));
225     inpsin->sin_port = 0;
226 
227     if (inp->inp_laddr.s_addr == INADDR_ANY)
228     {
229 	rtalloc(&inp->inp_route);
230 	if (rt = inp->inp_route.ro_rt)
231 	    ifp = rt->rt_ifp;
232     }
233     else
234     {
235 	if (rt = ip_route(&inp->inp_laddr, &sin->sin_addr))
236 	{
237 	    inp->inp_route.ro_rt = rt;
238 	    ifp = rt->rt_ifp;
239 	}
240     }
241 
242     if (ifp == NULL)
243 	return (ENETUNREACH);
244 
245     /*
246      * find Internet address structure for this interface.
247      */
248     ia = in_iafromif(ifp);
249 
250     if (ia == NULL)
251 	/* ??? */
252 	return (ENETUNREACH);
253 
254     ifaddr = (struct sockaddr_in *) &ia->ia_addr;
255 
256 #ifdef bsd42
257     /*
258      * 8.7.0.2 (on IMP net) can send to 128.11.0.0 (on Ethernet), but
259      * not to 8.0.0.0
260      */
261     if (in_broadcast(sin->sin_addr) &&
262 	iptonet(sin->sin_addr) == iptonet(ifaddr->sin_addr) &&
263 	!(ifp->if_flags & IFF_BROADCAST) )
264     {
265 	if (rt)
266 	{
267 	    rtfree(rt);
268 	    inp->inp_route.ro_rt = NULL;
269 	}
270 	return (EADDRNOTAVAIL);
271     }
272 #endif
273 
274     if ((*conn_used)(inp,
275 	inp->inp_lport,
276 	(inp->inp_laddr.s_addr ? inp->inp_laddr.s_addr : ifaddr->sin_addr.s_addr),
277 	sin->sin_port,
278 	sin->sin_addr.s_addr) != (char *)NULL)
279     {
280 
281 	if (rt)
282 	{
283 	    rtfree(rt);
284 	    inp->inp_route.ro_rt = NULL;
285 	}
286 	return (EADDRINUSE);
287     }
288 
289     if (inp->inp_laddr.s_addr == INADDR_ANY)
290 	inp->inp_laddr = ifaddr->sin_addr;
291     inp->inp_faddr = sin->sin_addr;
292     inp->inp_fport = sin->sin_port;
293     return (0);
294 }
295 
296 in_pcbdisconnect(inp, pcb_free_func)
297 struct inpcb *inp;
298 int (*pcb_free_func)();
299 {
300     inp->inp_faddr.s_addr = INADDR_ANY;
301     inp->inp_fport = 0;
302     /*
303      * may attach a route to an inpcb several times.  For example,
304      * when UDP does unconnected, but bound, sends.
305      */
306     if (inp->inp_route.ro_rt)
307     {
308 	rtfree(inp->inp_route.ro_rt);
309 	inp->inp_route.ro_rt = NULL;
310     }
311 
312     if (inp->inp_socket->so_state & SS_NOFDREF)
313 	in_pcbdetach(inp, pcb_free_func);
314 }
315 
316 /*
317  * Don't need to splnet while altering lists, since called from places
318  * where that has already been done.
319  */
320 in_pcbdetach(inp, pcb_free_func)
321 register struct inpcb *inp;
322 int (*pcb_free_func)();
323 {
324     register struct socket *so;
325 
326     if (so = inp->inp_socket)
327     {
328 	so->so_pcb = (caddr_t) NULL;
329 	/* inp->inp_socket = (struct socket *) NULL; */
330 	soisdisconnected(so);
331 	sofree(so);
332     }
333     else
334 	panic("in_pcbdetach");
335 
336     if (inp->inp_route.ro_rt)
337 	rtfree(inp->inp_route.ro_rt);
338 
339     if (inp->inp_ppcb)
340 	(*pcb_free_func)(inp); /* free per-protocol block */
341 
342     remque(inp);
343 
344     (void) m_free(dtom(inp));
345 }
346 
347 in_setsockaddr(inp, nam)
348 register struct inpcb *inp;
349 struct mbuf *nam;
350 {
351     register struct sockaddr_in *sin;
352 
353     nam->m_len = sizeof (*sin);
354     sin = mtod(nam, struct sockaddr_in *);
355     bzero((caddr_t)sin, sizeof (*sin));
356     sin->sin_family = AF_INET;
357     sin->sin_port = inp->inp_lport;
358     sin->sin_addr = inp->inp_laddr;
359 }
360 
361 in_setpeeraddr(inp, nam)
362 register struct inpcb *inp;
363 struct mbuf *nam;
364 {
365     register struct sockaddr_in *sin;
366 
367     nam->m_len = sizeof (*sin);
368     sin = mtod(nam, struct sockaddr_in *);
369     bzero((caddr_t)sin, sizeof (*sin));
370     sin->sin_family = AF_INET;
371     sin->sin_port = inp->inp_fport;
372     sin->sin_addr = inp->inp_faddr;
373 }
374 
375 /*
376  * somewhat different from the one in 4.2 and (I think) substantially
377  * easier to read, though a bit slower.
378  *
379  * fport == 0 if don't want/need match on remote port # (HMP and UDP)
380  */
381 struct inpcb *
382 in_pcblookup(head,faddr,fport,laddr,lport,wild)
383 struct inpcb *head;
384 u_long faddr, laddr;
385 u_short fport, lport;
386 int wild;
387 {
388     register struct inpcb *inp;
389 
390     /* try exact match */
391     for(inp = head->inp_next; inp != head; inp = inp->inp_next)
392     {
393 	/* ports check */
394 	if (inp->inp_lport != lport)
395 	    continue;
396 
397 	if (fport && (inp->inp_fport != fport))
398 	    continue;
399 
400 	if ((inp->inp_faddr.s_addr != faddr) || (inp->inp_laddr.s_addr != laddr))
401 	    continue;
402 
403 	/* keep it! */
404 	return(inp);
405     }
406 
407     /* try wildcard ? */
408     if (wild)
409     {
410 	for(inp = head->inp_next; inp != head; inp = inp->inp_next)
411 	{
412 	    /* ports again */
413 	    if (inp->inp_lport != lport)
414 		continue;
415 
416 	    if (fport && (inp->inp_fport != fport) && inp->inp_fport)
417 		continue;
418 
419 	    if ((inp->inp_faddr.s_addr) && (inp->inp_faddr.s_addr != faddr))
420 		continue;
421 
422 	    if ((inp->inp_laddr.s_addr) && (inp->inp_laddr.s_addr != laddr))
423 		continue;
424 
425 	    return(inp);
426 	}
427     }
428 
429     return((struct inpcb *) NULL);
430 }
431 
432 
433 /*
434  * This only advises process and does not internally close socket,
435  * not so much because the user can do much but close when he gets a
436  * HOSTDEAD/HOSTUNREACH indication, but because it is possible that
437  * the destination host has saved connection state information. (His IMP
438  * interface went down for PM, but the machine stayed up...)
439  *
440  * Also, this makes addition of new protocols easy, since we don't need to
441  * know the name and calling sequence of their close/abort routine.
442  *
443  * We do not close child sockets of listen(2)ers for connection oriented
444  * protocols.  We let the protocol do that by timing out connection
445  * establishment.
446  */
447 inpcb_notify(head, laddr, faddr, error)
448 register struct inpcb *head;
449 register u_long	laddr;
450 register u_long	faddr;
451 {
452     register struct inpcb   *inp;
453 
454     for(inp = head->inp_next; inp != head; inp = inp->inp_next)
455 	if (((inp->inp_faddr.s_addr == faddr) || (faddr == 0)) &&
456 	    ((inp->inp_laddr.s_addr == laddr) || (laddr == 0)))
457 		advise_user(inp->inp_socket, error);
458 }
459 
460 advise_user(so, error)
461 struct socket *so;
462 int error;
463 {
464     if (so == 0)
465 	return;
466 
467     so->so_error = error;
468 
469     wakeup((caddr_t) &so->so_timeo); /* in connect(2) */
470     sowwakeup(so);
471     sorwakeup(so);
472 }
473