1 /*-
2  * Copyright (c) 1999, 2001 Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Low level socket routines
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD: stable/9/sys/netncp/ncp_sock.c 206361 2010-04-07 16:50:38Z joel $");
31 
32 #include <sys/param.h>
33 #include <sys/errno.h>
34 #include <sys/lock.h>
35 #include <sys/malloc.h>
36 #include <sys/mutex.h>
37 #include <sys/systm.h>
38 #include <sys/proc.h>
39 #include <sys/socket.h>
40 #include <sys/socketvar.h>
41 #include <sys/protosw.h>
42 #include <sys/kernel.h>
43 #include <sys/uio.h>
44 #include <sys/syslog.h>
45 #include <sys/mbuf.h>
46 #include <sys/condvar.h>
47 #include <net/route.h>
48 
49 #include <netipx/ipx.h>
50 #include <netipx/ipx_pcb.h>
51 
52 #include <netncp/ncp.h>
53 #include <netncp/ncp_conn.h>
54 #include <netncp/ncp_sock.h>
55 #include <netncp/ncp_subr.h>
56 #include <netncp/ncp_rq.h>
57 
58 #define ipx_setnullnet(x) ((x).x_net.s_net[0]=0); ((x).x_net.s_net[1]=0);
59 #define ipx_setnullhost(x) ((x).x_host.s_host[0] = 0); \
60 	((x).x_host.s_host[1] = 0); ((x).x_host.s_host[2] = 0);
61 
62 /*static int ncp_getsockname(struct socket *so, caddr_t asa, int *alen);*/
63 static int ncp_soconnect(struct socket *so, struct sockaddr *target,
64 			 struct thread *td);
65 
66 
67 /* This will need only if native IP used, or (unlikely) NCP will be
68  * implemented on the socket level
69  */
70 static int
ncp_soconnect(struct socket * so,struct sockaddr * target,struct thread * td)71 ncp_soconnect(struct socket *so, struct sockaddr *target, struct thread *td)
72 {
73 	int error, s;
74 
75 	error = soconnect(so, (struct sockaddr*)target, td);
76 	if (error)
77 		return error;
78 	/*
79 	 * Wait for the connection to complete. Cribbed from the
80 	 * connect system call but with the wait timing out so
81 	 * that interruptible mounts don't hang here for a long time.
82 	 */
83 	error = EIO;
84 	s = splnet();
85 	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
86 		(void) tsleep((caddr_t)&so->so_timeo, PSOCK, "ncpcon", 2 * hz);
87 		if ((so->so_state & SS_ISCONNECTING) &&
88 		    so->so_error == 0 /*&& rep &&*/) {
89 			so->so_state &= ~SS_ISCONNECTING;
90 			splx(s);
91 			goto bad;
92 		}
93 	}
94 	if (so->so_error) {
95 		error = so->so_error;
96 		so->so_error = 0;
97 		splx(s);
98 		goto bad;
99 	}
100 		splx(s);
101 	error=0;
102 bad:
103 	return error;
104 }
105 #ifdef notyet
106 static int
ncp_getsockname(struct socket * so,caddr_t asa,int * alen)107 ncp_getsockname(struct socket *so, caddr_t asa, int *alen) {
108 	struct sockaddr *sa;
109 	int len=0, error;
110 
111 	sa = 0;
112 	error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, &sa);
113 	if (error==0) {
114 		if (sa) {
115 			len = min(len, sa->sa_len);
116 			bcopy(sa, (caddr_t)asa, (u_int)len);
117 		}
118 		*alen=len;
119 	}
120 	if (sa)
121 		free(sa, M_SONAME);
122 	return (error);
123 }
124 #endif
ncp_sock_recv(struct socket * so,struct mbuf ** mp,int * rlen)125 int ncp_sock_recv(struct socket *so, struct mbuf **mp, int *rlen)
126 {
127 	struct uio auio;
128 	struct thread *td = curthread; /* XXX */
129 	int error,flags,len;
130 
131 	auio.uio_resid = len = 1000000;
132 	auio.uio_td = td;
133 	flags = MSG_DONTWAIT;
134 
135 /*	error = soreceive(so, 0, &auio, (struct mbuf **)0, (struct mbuf **)0,
136 	    &flags);*/
137 	error = soreceive(so, 0, &auio, mp, (struct mbuf **)0, &flags);
138 	*rlen = len - auio.uio_resid;
139 /*	if (!error) {
140 	    *rlen=iov.iov_len;
141 	} else
142 	    *rlen=0;*/
143 #ifdef NCP_SOCKET_DEBUG
144 	if (error)
145 		printf("ncp_recv: err=%d\n", error);
146 #endif
147 	return (error);
148 }
149 
150 int
ncp_sock_send(struct socket * so,struct mbuf * top,struct ncp_rq * rqp)151 ncp_sock_send(struct socket *so, struct mbuf *top, struct ncp_rq *rqp)
152 {
153 	struct thread *td = curthread; /* XXX */
154 	struct sockaddr *to = 0;
155 	struct ncp_conn *conn = rqp->nr_conn;
156 	struct mbuf *m;
157 	int error, flags=0;
158 
159 	for (;;) {
160 		m = m_copym(top, 0, M_COPYALL, M_WAIT);
161 /*		NCPDDEBUG(m);*/
162 		error = sosend(so, to, 0, m, 0, flags, td);
163 		if (error == 0 || error == EINTR || error == ENETDOWN)
164 			break;
165 		if (rqp->rexmit == 0) break;
166 		rqp->rexmit--;
167 		pause("ncprsn", conn->li.timeout * hz);
168 		error = ncp_chkintr(conn, td);
169 		if (error == EINTR) break;
170 	}
171 	if (error) {
172 		log(LOG_INFO, "ncp_send: error %d for server %s", error, conn->li.server);
173 	}
174 	return error;
175 }
176 
177 /*
178  * Connect to specified server via IPX
179  */
180 static int
ncp_sock_connect_ipx(struct ncp_conn * conn)181 ncp_sock_connect_ipx(struct ncp_conn *conn)
182 {
183 	struct sockaddr_ipx sipx;
184 	struct ipxpcb *npcb;
185 	struct thread *td = conn->td;
186 	int addrlen, error, count;
187 
188 	sipx.sipx_port = htons(0);
189 
190 	for (count = 0;;count++) {
191 		if (count > (IPXPORT_WELLKNOWN-IPXPORT_RESERVED)*2) {
192 			error = EADDRINUSE;
193 			goto bad;
194 		}
195 		conn->ncp_so = conn->wdg_so = NULL;
196 		checkbad(socreate(AF_IPX, &conn->ncp_so, SOCK_DGRAM, 0, td->td_ucred, td));
197 		if (conn->li.opt & NCP_OPT_WDOG)
198 			checkbad(socreate(AF_IPX, &conn->wdg_so, SOCK_DGRAM, 0, td->td_ucred, td));
199 		addrlen = sizeof(sipx);
200 		sipx.sipx_family = AF_IPX;
201 		ipx_setnullnet(sipx.sipx_addr);
202 		ipx_setnullhost(sipx.sipx_addr);
203 		sipx.sipx_len = addrlen;
204 		error = sobind(conn->ncp_so, (struct sockaddr *)&sipx, td);
205 		if (error == 0) {
206 			if ((conn->li.opt & NCP_OPT_WDOG) == 0)
207 				break;
208 			sipx.sipx_addr = sotoipxpcb(conn->ncp_so)->ipxp_laddr;
209 			sipx.sipx_port = htons(ntohs(sipx.sipx_port) + 1);
210 			ipx_setnullnet(sipx.sipx_addr);
211 			ipx_setnullhost(sipx.sipx_addr);
212 			error = sobind(conn->wdg_so, (struct sockaddr *)&sipx, td);
213 		}
214 		if (!error) break;
215 		if (error != EADDRINUSE) goto bad;
216 		sipx.sipx_port = htons((ntohs(sipx.sipx_port)+4) & 0xfff8);
217 		soclose(conn->ncp_so);
218 		if (conn->wdg_so)
219 			soclose(conn->wdg_so);
220 	}
221 	npcb = sotoipxpcb(conn->ncp_so);
222 	npcb->ipxp_dpt = IPXPROTO_NCP;
223 	/* IPXrouted must be running, i.e. route must be presented */
224 	conn->li.ipxaddr.sipx_len = sizeof(struct sockaddr_ipx);
225 	checkbad(ncp_soconnect(conn->ncp_so, &conn->li.saddr, td));
226 	if (conn->wdg_so) {
227 		sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_net = npcb->ipxp_laddr.x_net;
228 		sotoipxpcb(conn->wdg_so)->ipxp_laddr.x_host= npcb->ipxp_laddr.x_host;
229 	}
230 	if (!error) {
231 		conn->flags |= NCPFL_SOCONN;
232 	}
233 #ifdef NCPBURST
234 	if (ncp_burst_enabled) {
235 		checkbad(socreate(AF_IPX, &conn->bc_so, SOCK_DGRAM, 0, td));
236 		bzero(&sipx, sizeof(sipx));
237 		sipx.sipx_len = sizeof(sipx);
238 		checkbad(sobind(conn->bc_so, (struct sockaddr *)&sipx, td));
239 		checkbad(ncp_soconnect(conn->bc_so, &conn->li.saddr, td));
240 	}
241 #endif
242 	if (!error) {
243 		conn->flags |= NCPFL_SOCONN;
244 		ncp_sock_checksum(conn, 0);
245 	}
246 	return error;
247 bad:
248 	ncp_sock_disconnect(conn);
249 	return (error);
250 }
251 
252 int
ncp_sock_checksum(struct ncp_conn * conn,int enable)253 ncp_sock_checksum(struct ncp_conn *conn, int enable)
254 {
255 
256 	if (enable) {
257 		sotoipxpcb(conn->ncp_so)->ipxp_flags |= IPXP_CHECKSUM;
258 	} else {
259 		sotoipxpcb(conn->ncp_so)->ipxp_flags &= ~IPXP_CHECKSUM;
260 	}
261 	return 0;
262 }
263 
264 /*
265  * Connect to specified server via IP
266  */
267 static int
ncp_sock_connect_in(struct ncp_conn * conn)268 ncp_sock_connect_in(struct ncp_conn *conn)
269 {
270 	struct sockaddr_in sin;
271 	struct thread *td = conn->td;
272 	int addrlen = sizeof(sin), error;
273 
274 	conn->flags = 0;
275 	bzero(&sin,addrlen);
276 	conn->ncp_so = conn->wdg_so = NULL;
277 	checkbad(socreate(AF_INET, &conn->ncp_so, SOCK_DGRAM, IPPROTO_UDP, td->td_ucred, td));
278 	sin.sin_family = AF_INET;
279 	sin.sin_len = addrlen;
280 	checkbad(sobind(conn->ncp_so, (struct sockaddr *)&sin, td));
281 	checkbad(ncp_soconnect(conn->ncp_so,(struct sockaddr*)&conn->li.addr, td));
282 	if  (!error)
283 		conn->flags |= NCPFL_SOCONN;
284 	return error;
285 bad:
286 	ncp_sock_disconnect(conn);
287 	return (error);
288 }
289 
290 int
ncp_sock_connect(struct ncp_conn * ncp)291 ncp_sock_connect(struct ncp_conn *ncp)
292 {
293 	int error;
294 
295 	switch (ncp->li.saddr.sa_family) {
296 	    case AF_IPX:
297 		error = ncp_sock_connect_ipx(ncp);
298 		break;
299 	    case AF_INET:
300 		error = ncp_sock_connect_in(ncp);
301 		break;
302 	    default:
303 		return EPROTONOSUPPORT;
304 	}
305 	return error;
306 }
307 
308 /*
309  * Connection expected to be locked
310  */
311 int
ncp_sock_disconnect(struct ncp_conn * conn)312 ncp_sock_disconnect(struct ncp_conn *conn) {
313 	register struct socket *so;
314 	conn->flags &= ~(NCPFL_SOCONN | NCPFL_ATTACHED | NCPFL_LOGGED);
315 	if (conn->ncp_so) {
316 		so = conn->ncp_so;
317 		conn->ncp_so = (struct socket *)0;
318 		soshutdown(so, 2);
319 		soclose(so);
320 	}
321 	if (conn->wdg_so) {
322 		so = conn->wdg_so;
323 		conn->wdg_so = (struct socket *)0;
324 		soshutdown(so, 2);
325 		soclose(so);
326 	}
327 #ifdef NCPBURST
328 	if (conn->bc_so) {
329 		so = conn->bc_so;
330 		conn->bc_so = (struct socket *)NULL;
331 		soshutdown(so, 2);
332 		soclose(so);
333 	}
334 #endif
335 	return 0;
336 }
337 
338 static void
ncp_watchdog(struct ncp_conn * conn)339 ncp_watchdog(struct ncp_conn *conn) {
340 	char *buf;
341 	struct mbuf *m;
342 	int error, len, flags;
343 	struct socket *so;
344 	struct sockaddr *sa;
345 	struct uio auio;
346 
347 	sa = NULL;
348 	while (conn->wdg_so) { /* not a loop */
349 		so = conn->wdg_so;
350 		auio.uio_resid = len = 1000000;
351 		auio.uio_td = curthread;
352 		flags = MSG_DONTWAIT;
353 		error = soreceive(so, (struct sockaddr**)&sa, &auio, &m,
354 		    (struct mbuf**)0, &flags);
355 		if (error) break;
356 		len -= auio.uio_resid;
357 		NCPSDEBUG("got watch dog %d\n",len);
358 		if (len != 2) break;
359 		buf = mtod(m, char*);
360 		if (buf[1] != '?') break;
361 		buf[1] = 'Y';
362 		error = sosend(so, (struct sockaddr*)sa, 0, m, 0, 0, curthread);
363 		NCPSDEBUG("send watch dog %d\n",error);
364 		break;
365 	}
366 	if (sa) free(sa, M_SONAME);
367 	return;
368 }
369 
370 void
ncp_check_conn(struct ncp_conn * conn)371 ncp_check_conn(struct ncp_conn *conn) {
372 	int s;
373 
374 	if (conn == NULL || !(conn->flags & NCPFL_ATTACHED))
375 	        return;
376 	s = splnet();
377 	ncp_check_rq(conn);
378 	splx(s);
379 	if (conn->li.saddr.sa_family == AF_IPX)
380 		ncp_watchdog(conn);
381 }
382