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$");
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);*/
63static 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 */
70static int
71ncp_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;
102bad:
103	return error;
104}
105#ifdef notyet
106static int
107ncp_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
125int 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
150int
151ncp_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 */
180static int
181ncp_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;
247bad:
248	ncp_sock_disconnect(conn);
249	return (error);
250}
251
252int
253ncp_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 */
267static int
268ncp_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;
285bad:
286	ncp_sock_disconnect(conn);
287	return (error);
288}
289
290int
291ncp_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 */
311int
312ncp_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
338static void
339ncp_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
370void
371ncp_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