bounds checking
[unix-history] / usr / src / sys / netinet / in_pcb.c
... / ...
CommitLineData
1/* in_pcb.c 4.22 82/03/28 */
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/dir.h"
6#include "../h/user.h"
7#include "../h/mbuf.h"
8#include "../h/socket.h"
9#include "../h/socketvar.h"
10#include "../net/in.h"
11#include "../net/in_systm.h"
12#include "../net/if.h"
13#include "../net/in_pcb.h"
14#include "../h/protosw.h"
15
16/*
17 * Routines to manage internet protocol control blocks.
18 *
19 * At PRU_ATTACH time a protocol control block is allocated in
20 * in_pcballoc() and inserted on a doubly-linked list of such blocks
21 * for the protocol. A port address is either requested (and verified
22 * to not be in use) or assigned at this time. We also allocate
23 * space in the socket sockbuf structures here, although this is
24 * not a clearly correct place to put this function.
25 *
26 * A connectionless protocol will have its protocol control block
27 * removed at PRU_DETACH time, when the socket will be freed (freeing
28 * the space reserved) and the block will be removed from the list of
29 * blocks for its protocol.
30 *
31 * A connection-based protocol may be connected to a remote peer at
32 * PRU_CONNECT time through the routine in_pcbconnect(). In the normal
33 * case a PRU_DISCONNECT occurs causing a in_pcbdisconnect().
34 * It is also possible that higher-level routines will opt out of the
35 * relationship with the connection before the connection shut down
36 * is complete. This often occurs in protocols like TCP where we must
37 * hold on to the protocol control block for a unreasonably long time
38 * after the connection is used up to avoid races in later connection
39 * establishment. To handle this we allow higher-level routines to
40 * disassociate themselves from the socket, marking it SS_USERGONE while
41 * the disconnect is in progress. We notice that this has happened
42 * when the disconnect is complete, and perform the PRU_DETACH operation,
43 * freeing the socket.
44 *
45 * TODO:
46 * use hashing
47 */
48struct in_addr zeroin_addr;
49
50/*
51 * Allocate a protocol control block, space
52 * for send and receive data, and local host information.
53 * Return error. If no error make socket point at pcb.
54 */
55in_pcbattach(so, head, sndcc, rcvcc, sin)
56 struct socket *so;
57 struct inpcb *head;
58 int sndcc, rcvcc;
59 struct sockaddr_in *sin;
60{
61 struct mbuf *m;
62 register struct inpcb *inp;
63 u_short lport = 0;
64
65COUNT(IN_PCBATTACH);
66 if (ifnet == 0)
67 return (EADDRNOTAVAIL);
68 if (sin) {
69 if (sin->sin_family != AF_INET)
70 return (EAFNOSUPPORT);
71 if (sin->sin_addr.s_addr &&
72 if_ifwithaddr((struct sockaddr *)sin) == 0)
73 return (EADDRNOTAVAIL);
74 lport = sin->sin_port;
75 if (lport) {
76 u_short aport = lport;
77 int wild = 0;
78#if vax
79 aport = htons(aport);
80#endif
81 /* GROSS */
82 if (aport < IPPORT_RESERVED && u.u_uid != 0)
83 return (EPERM);
84 if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
85 (so->so_options & SO_ACCEPTCONN) == 0)
86 wild = INPLOOKUP_WILDCARD;
87 if (in_pcblookup(head,
88 zeroin_addr, 0, sin->sin_addr, lport, wild))
89 return (EADDRINUSE);
90 }
91 }
92 m = m_getclr(M_DONTWAIT);
93 if (m == 0)
94 return (ENOBUFS);
95 if (sbreserve(&so->so_snd, sndcc) == 0)
96 goto bad;
97 if (sbreserve(&so->so_rcv, rcvcc) == 0)
98 goto bad2;
99 inp = mtod(m, struct inpcb *);
100 inp->inp_head = head;
101 if (sin)
102 inp->inp_laddr = sin->sin_addr;
103 if (lport == 0)
104 do {
105 if (head->inp_lport++ < IPPORT_RESERVED)
106 head->inp_lport = IPPORT_RESERVED;
107 lport = htons(head->inp_lport);
108 } while (in_pcblookup(head,
109 zeroin_addr, 0, inp->inp_laddr, lport, 0));
110 inp->inp_lport = lport;
111 inp->inp_socket = so;
112 insque(inp, head);
113 so->so_pcb = (caddr_t)inp;
114 in_setsockaddr(inp);
115 return (0);
116bad2:
117 sbrelease(&so->so_snd);
118bad:
119 (void) m_free(m);
120 return (ENOBUFS);
121}
122
123/*
124 * Connect from a socket to a specified address.
125 * Both address and port must be specified in argument sin.
126 * If don't have a local address for this socket yet,
127 * then pick one.
128 */
129in_pcbconnect(inp, sin)
130 struct inpcb *inp;
131 struct sockaddr_in *sin;
132{
133 struct ifnet *ifp;
134 struct sockaddr_in *ifaddr;
135
136COUNT(IN_PCBCONNECT);
137 if (sin->sin_family != AF_INET)
138 return (EAFNOSUPPORT);
139 if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
140 return (EADDRNOTAVAIL);
141 if (inp->inp_laddr.s_addr == 0) {
142 ifp = if_ifonnetof(sin->sin_addr.s_net);
143 if (ifp == 0) {
144 ifp = if_ifwithaf(AF_INET);
145 if (ifp == 0)
146 return (EADDRNOTAVAIL); /* XXX */
147 }
148 ifaddr = (struct sockaddr_in *)&ifp->if_addr;
149 }
150 if (in_pcblookup(inp->inp_head,
151 sin->sin_addr,
152 sin->sin_port,
153 inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
154 inp->inp_lport,
155 0))
156 return (EADDRINUSE);
157 if (inp->inp_laddr.s_addr == 0) {
158 struct sockaddr_in *sin2 =
159 (struct sockaddr_in *)&inp->inp_socket->so_addr;
160
161 inp->inp_laddr = ifaddr->sin_addr;
162 sin2->sin_addr = inp->inp_laddr;
163 }
164 inp->inp_faddr = sin->sin_addr;
165 inp->inp_fport = sin->sin_port;
166 return (0);
167}
168
169in_setsockaddr(inp)
170 struct inpcb *inp;
171{
172 register struct sockaddr_in *sin =
173 (struct sockaddr_in *)&inp->inp_socket->so_addr;
174
175 sin->sin_family = AF_INET;
176 sin->sin_addr = inp->inp_laddr;
177 sin->sin_port = inp->inp_lport;
178}
179
180in_pcbdisconnect(inp)
181 struct inpcb *inp;
182{
183
184COUNT(IN_PCBDISCONNECT);
185 inp->inp_faddr.s_addr = 0;
186 inp->inp_fport = 0;
187 if (inp->inp_socket->so_state & SS_USERGONE)
188 in_pcbdetach(inp);
189}
190
191in_pcbdetach(inp)
192 struct inpcb *inp;
193{
194 struct socket *so = inp->inp_socket;
195
196 so->so_pcb = 0;
197 sofree(so);
198 remque(inp);
199 (void) m_free(dtom(inp));
200}
201
202/*
203 * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
204 */
205struct inpcb *
206in_pcblookup(head, faddr, fport, laddr, lport, flags)
207 struct inpcb *head;
208 struct in_addr faddr, laddr;
209 u_short fport, lport;
210 int flags;
211{
212 register struct inpcb *inp, *match = 0;
213 int matchwild = 3, wildcard;
214
215 for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
216 if (inp->inp_lport != lport)
217 continue;
218 wildcard = 0;
219 if (inp->inp_laddr.s_addr != 0) {
220 if (laddr.s_addr == 0)
221 wildcard++;
222 else if (inp->inp_laddr.s_addr != laddr.s_addr)
223 continue;
224 } else {
225 if (laddr.s_addr != 0)
226 wildcard++;
227 }
228 if (inp->inp_faddr.s_addr != 0) {
229 if (faddr.s_addr == 0)
230 wildcard++;
231 else if (inp->inp_faddr.s_addr != faddr.s_addr ||
232 inp->inp_fport != fport)
233 continue;
234 } else {
235 if (faddr.s_addr != 0)
236 wildcard++;
237 }
238 if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
239 continue;
240 if (wildcard < matchwild) {
241 match = inp;
242 matchwild = wildcard;
243 if (matchwild == 0)
244 break;
245 }
246 }
247 return (match);
248}