proc->thread stage 4: rework the VFS and DEVICE subsystems to take thread
[dragonfly.git] / sys / netproto / ncp / ncp_ncp.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 1999, 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 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Boris Popov.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: src/sys/netncp/ncp_ncp.c,v 1.3 1999/10/29 10:21:07 bp Exp $
33 * $DragonFly: src/sys/netproto/ncp/ncp_ncp.c,v 1.3 2003/06/25 03:56:05 dillon Exp $
34 *
35 * Core of NCP protocol
36 */
37#include "opt_inet.h"
38#include "opt_ipx.h"
39
40#include <sys/param.h>
41#include <sys/errno.h>
42#include <sys/systm.h>
43#include <sys/proc.h>
44#include <sys/poll.h>
45#include <sys/signalvar.h>
46#include <sys/mbuf.h>
47
48#ifdef IPX
49#include <netipx/ipx.h>
50#include <netipx/ipx_var.h>
51#endif
52
53#include <netncp/ncp.h>
54#include <netncp/ncp_conn.h>
55#include <netncp/ncp_sock.h>
56#include <netncp/ncp_subr.h>
57#include <netncp/ncp_ncp.h>
58#include <netncp/ncp_rq.h>
59#include <netncp/nwerror.h>
60
61static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp);
62static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target);
63static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options);
64static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
65
66
67#ifdef NCP_DATA_DEBUG
68static
69void m_dumpm(struct mbuf *m) {
70 char *p;
71 int len;
72 printf("d=");
73 while(m) {
74 p=mtod(m,char *);
75 len=m->m_len;
76 printf("(%d)",len);
77 while(len--){
78 printf("%02x ",((int)*(p++)) & 0xff);
79 }
80 m=m->m_next;
81 };
82 printf("\n");
83}
84#endif /* NCP_DATA_DEBUG */
85
86int
87ncp_chkintr(struct ncp_conn *conn, struct thread *td)
88{
89 sigset_t tmpset;
90 struct proc *p = td->td_proc;
91
92 if (p == NULL)
93 return 0;
94 tmpset = p->p_siglist;
95 SIGSETNAND(tmpset, p->p_sigmask);
96 SIGSETNAND(tmpset, p->p_sigignore);
97 if (SIGNOTEMPTY(p->p_siglist) && NCP_SIGMASK(tmpset))
98 return EINTR;
99 return 0;
100}
101
102/*
103 * Process initial NCP handshake (attach)
104 * NOTE: Since all functions below may change conn attributes, they
105 * should be called with LOCKED connection, also they use td & ucred
106 */
107int
108ncp_ncp_connect(struct ncp_conn *conn) {
109 int error;
110 struct ncp_rphdr *rp;
111 DECLARE_RQ;
112
113 conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
114 conn->seq = 0;
115 checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->td,conn->ucred));
116 error=ncp_do_request(conn,rqp);
117 if (!error) {
118 rp = mtod(rqp->rp, struct ncp_rphdr*);
119 conn->connid = rp->conn_low + (rp->conn_high << 8);
120 }
121 ncp_rq_done(rqp);
122 if (error) return error;
123 conn->flags |= NCPFL_ATTACHED;
124
125 error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0);
126 if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
127 printf("Unable to negotiate requested security level\n");
128 error = EOPNOTSUPP;
129 }
130 if (error) {
131 ncp_ncp_disconnect(conn);
132 return error;
133 }
134#ifdef NCPBURST
135 ncp_burst_connect(conn);
136#endif
137bad:
138 return error;
139}
140
141int
142ncp_ncp_disconnect(struct ncp_conn *conn) {
143 int error;
144 struct ncp_rqhdr *ncprq;
145 DECLARE_RQ;
146
147 NCPSDEBUG("for connid=%d\n",conn->nc_id);
148#ifdef NCPBURST
149 ncp_burst_disconnect(conn);
150#endif
151 error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->td,conn->ucred);
152 ncprq = mtod(rqp->rq,struct ncp_rqhdr*);
153 error=ncp_do_request(conn,rqp);
154 ncp_rq_done(rqp);
155 ncp_conn_invalidate(conn);
156 ncp_sock_disconnect(conn);
157 return 0;
158}
159/*
160 * Make a signature for the current packet and add it at the end of the
161 * packet.
162 */
163static void
164ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
165 u_char data[64];
166
167 bzero(data, sizeof(data));
168 bcopy(conn->sign_root, data, 8);
169 setdle(data, 8, *size);
170 m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1,
171 min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12);
172 ncp_sign(conn->sign_state, data, conn->sign_state);
173 ncp_rq_mem(rqp, (void*)conn->sign_state, 8);
174 (*size) += 8;
175}
176
177/*
178 * Low level send rpc, here we do not attempt to restore any connection,
179 * Connection expected to be locked
180 */
181static int
182ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
183 int error=EIO,len, dosend, plen = 0, gotpacket, s;
184 struct socket *so;
185 struct thread *td = conn->td;
186 struct ncp_rqhdr *rq;
187 struct ncp_rphdr *rp=NULL;
188 struct timeval tv;
189 struct mbuf *m, *mreply = NULL;
190
191 conn->nc_rq = rqp;
192 rqp->conn = conn;
193 if (td == NULL)
194 td = curthread; /* XXX maybe procpage ? */
195 if (!ncp_conn_valid(conn)) {
196 printf("%s: conn not valid\n",__FUNCTION__);
197 return (error);
198 }
199 so = conn->ncp_so;
200 if (!so) {
201 printf("%s: ncp_so is NULL !\n",__FUNCTION__);
202 ncp_conn_invalidate(conn); /* wow ! how we do that ? */
203 return EBADF;
204 }
205 /*
206 * Flush out replies on previous reqs
207 */
208 s = splnet();
209 while (1/*so->so_rcv.sb_cc*/) {
210 if (ncp_poll(so,POLLIN) == 0) break;
211 if (ncp_sock_recv(so,&m,&len) != 0) break;
212 m_freem(m);
213 }
214 rq = mtod(rqp->rq,struct ncp_rqhdr *);
215 rq->seq = conn->seq;
216 m = rqp->rq;
217 len = 0;
218 while (m) {
219 len += m->m_len;
220 m = m->m_next;
221 }
222 rqp->rq->m_pkthdr.len = len;
223 switch(rq->fn) {
224 case 0x15: case 0x16: case 0x17: case 0x23:
225 m = rqp->rq;
226 *((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
227 break;
228 }
229 if (conn->flags & NCPFL_SIGNACTIVE) {
230 ncp_sign_packet(conn, rqp, &len);
231 rqp->rq->m_pkthdr.len = len;
232 }
233 rq->conn_low = conn->connid & 0xff;
234 /* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
235 /* XXX: this is temporary fix till I find a better solution */
236 rq->task = rq->conn_low;
237 rq->conn_high = conn->connid >> 8;
238 rqp->rexmit = conn->li.retry_count;
239 for(dosend = 1;;) {
240 if (rqp->rexmit-- == 0) {
241 error = ETIMEDOUT;
242 break;
243 }
244 error = 0;
245 if (dosend) {
246 NCPSDEBUG("send:%04x f=%02x c=%d l=%d s=%d t=%d\n",rq->type, rq->fn, (rq->conn_high << 8) + rq->conn_low,
247 rqp->rq->m_pkthdr.len, rq->seq, rq->task
248 );
249 error = ncp_sock_send(so, rqp->rq, rqp);
250 if (error) break;
251 }
252 tv.tv_sec = conn->li.timeout;
253 tv.tv_usec = 0;
254 error = ncp_sock_rselect(so, td, &tv, POLLIN);
255 if (error == EWOULDBLOCK ) /* timeout expired */
256 continue;
257 error = ncp_chkintr(conn, td);
258 if (error == EINTR) /* we dont restart */
259 break;
260 if (error) break;
261 /*
262 * At this point it is possible to get more than one
263 * reply from server. In general, last reply should be for
264 * current request, but not always. So, we loop through
265 * all replies to find the right answer and flush others.
266 */
267 gotpacket = 0; /* nothing good found */
268 dosend = 1; /* resend rq if error */
269 for (;;) {
270 error = 0;
271 if (ncp_poll(so,POLLIN) == 0) break;
272/* if (so->so_rcv.sb_cc == 0) {
273 break;
274 }*/
275 error = ncp_sock_recv(so,&m,&len);
276 if (error) break; /* must be more checks !!! */
277 if (m->m_len < sizeof(*rp)) {
278 m = m_pullup(m, sizeof(*rp));
279 if (m == NULL) {
280 printf("%s: reply too short\n",__FUNCTION__);
281 continue;
282 }
283 }
284 rp = mtod(m, struct ncp_rphdr*);
285 if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
286 NCPSDEBUG("got positive acknowledge\n");
287 m_freem(m);
288 rqp->rexmit = conn->li.retry_count;
289 dosend = 0; /* server just busy and will reply ASAP */
290 continue;
291 }
292 NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
293 (rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
294 rp->completion_code, rp->connection_state);
295 NCPDDEBUG(m);
296 if ( (rp->type == NCP_REPLY) &&
297 ((rq->type == NCP_ALLOC_SLOT) ||
298 ((rp->conn_low == rq->conn_low) &&
299 (rp->conn_high == rq->conn_high)
300 ))) {
301 if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
302 dosend = 1;
303 }
304 if (rp->seq == rq->seq) {
305 if (gotpacket) {
306 m_freem(m);
307 } else {
308 gotpacket = 1;
309 mreply = m;
310 plen = len;
311 }
312 continue; /* look up other for other packets */
313 }
314 }
315 m_freem(m);
316 NCPSDEBUG("reply mismatch\n");
317 } /* for receive */
318 if (error) break;
319 if (gotpacket) break;
320 /* try to resend, or just wait */
321 }
322 splx(s);
323 conn->seq++;
324 if (error) {
325 NCPSDEBUG("error=%d\n",error);
326 if (error != EINTR) /* if not just interrupt */
327 ncp_conn_invalidate(conn); /* only reconnect to restore */
328 return(error);
329 }
330 if (conn->flags & NCPFL_SIGNACTIVE) {
331 /* XXX: check reply signature */
332 m_adj(mreply, -8);
333 plen -= 8;
334 }
335 len = plen;
336 m = mreply;
337 rp = mtod(m, struct ncp_rphdr*);
338 len -= sizeof(*rp);
339 rqp->rpsize = len;
340 rqp->cc = error = rp->completion_code;
341 if (error) error |= 0x8900; /* server error */
342 rqp->cs = rp->connection_state;
343 if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
344 NCPSDEBUG("server drop us\n");
345 ncp_conn_invalidate(conn);
346 error = ECONNRESET;
347 }
348 rqp->rp = m;
349 rqp->mrp = m;
350 rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
351 return error;
352}
353
354/*
355 * Here we will try to restore any loggedin & dropped connection,
356 * connection should be locked on entry
357 */
358int ncp_restore_login(struct ncp_conn *conn);
359int
360ncp_restore_login(struct ncp_conn *conn) {
361 int error, oldflags;
362
363 if (conn->flags & NCPFL_RESTORING) {
364 printf("Hey, ncp_restore_login called twise !!!\n");
365 return 0;
366 }
367 oldflags = conn->flags;
368 printf("Restoring connection, flags = %d\n",oldflags);
369 if ((oldflags & NCPFL_LOGGED) == 0) {
370 return ECONNRESET; /* no need to restore empty conn */
371 }
372 conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
373 conn->flags |= NCPFL_RESTORING;
374 do { /* not a loop */
375 error = ncp_reconnect(conn);
376 if (error) break;
377 if (conn->li.user)
378 error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->td,conn->ucred);
379 if (error) break;
380 conn->flags |= NCPFL_LOGGED;
381 } while(0);
382 if (error) {
383 conn->flags = oldflags | NCPFL_INVALID;
384 }
385 conn->flags &= ~NCPFL_RESTORING;
386 return error;
387}
388
389int
390ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
391 int error, rcnt;
392/* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
393
394 error = ncp_conn_lock(conn,rqp->td,rqp->cred,NCPM_EXECUTE);
395 if (error) return error;
396 rcnt = NCP_RESTORE_COUNT;
397 for(;;) {
398 if (!ncp_conn_valid(conn)) {
399 if (rcnt==0) {
400 error = ECONNRESET;
401 break;
402 }
403 rcnt--;
404 error = ncp_restore_login(conn);
405 if (error)
406 continue;
407 }
408 error=ncp_do_request(conn, rqp);
409 if (ncp_conn_valid(conn)) /* not just error ! */
410 break;
411 }
412 ncp_conn_unlock(conn,rqp->td);
413 return error;
414}
415
416/*
417 * All negotiation functions expect a locked connection
418 */
419static int
420ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
421 int error;
422 DECLARE_RQ;
423
424 NCP_RQ_HEAD(0x21,conn->td,conn->ucred);
425 ncp_rq_word_hl(rqp, size);
426 checkbad(ncp_request(conn,rqp));
427 *target = min(ncp_rp_word_hl(rqp), size);
428 NCP_RQ_EXIT;
429 return error;
430}
431
432static int
433ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
434 int *ret_size, int *ret_options) {
435 int error;
436 int rs;
437 DECLARE_RQ;
438
439 NCP_RQ_HEAD(0x61,conn->td,conn->ucred);
440 ncp_rq_word_hl(rqp, size);
441 ncp_rq_byte(rqp, options);
442 checkbad(ncp_request(conn, rqp));
443 rs = ncp_rp_word_hl(rqp);
444 *ret_size = (rs == 0) ? size : min(rs, size);
445 ncp_rp_word_hl(rqp); /* skip echo socket */
446 *ret_options = ncp_rp_byte(rqp);
447 NCP_RQ_EXIT;
448 return error;
449}
450
451static int
452ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
453{
454 int neg_buffsize, error, options, sl;
455
456 sl = conn->li.sig_level;
457 if (sl >= 2)
458 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
459#ifdef IPX
460 if (ipxcksum == 2)
461 in_options |= NCP_IPX_CHECKSUM;
462#endif
463 error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
464 &neg_buffsize, &options);
465 if (!error) {
466#ifdef IPX
467 if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
468 if (ipxcksum == 2) {
469 printf("Server refuses to support IPX checksums\n");
470 return NWE_REQUESTER_FAILURE;
471 }
472 in_options |= NCP_IPX_CHECKSUM;
473 error = 1;
474 }
475#endif /* IPX */
476 if ((options ^ in_options) & 2) {
477 if (sl == 0 || sl == 3)
478 return NWE_SIGNATURE_LEVEL_CONFLICT;
479 if (sl == 1) {
480 in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
481 error = 1;
482 }
483 }
484 if (error) {
485 error = ncp_negotiate_size_and_options(conn,
486 buffsize, in_options, &neg_buffsize, &options);
487 if ((options ^ in_options) & 3) {
488 return NWE_SIGNATURE_LEVEL_CONFLICT;
489 }
490 }
491 } else {
492 in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
493 error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
494 &neg_buffsize);
495 }
496 if (error) return error;
497 if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
498 return EINVAL;
499 conn->buffer_size = neg_buffsize;
500 if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
501 conn->flags |= NCPFL_SIGNWANTED;
502#ifdef IPX
503 ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
504#endif
505 return 0;
506}
507
508int
509ncp_reconnect(struct ncp_conn *conn) {
510 int error;
511
512 /* close any open sockets */
513 ncp_sock_disconnect(conn);
514 switch( conn->li.saddr.sa_family ) {
515#ifdef IPX
516 case AF_IPX:
517 error = ncp_sock_connect_ipx(conn);
518 break;
519#endif
520#ifdef INET
521 case AF_INET:
522 error = ncp_sock_connect_in(conn);
523 break;
524#endif
525 default:
526 return EPROTONOSUPPORT;
527 }
528 if (!error)
529 error = ncp_ncp_connect(conn);
530 return error;
531}
532
533/*
534 * Create conn structure and try to do low level connect
535 * Server addr should be filled in.
536 */
537int
538ncp_connect(struct ncp_conn_args *li, struct thread *td, struct ucred *cred,
539 struct ncp_conn **aconn)
540{
541 struct ncp_conn *conn;
542 struct ucred *owner;
543 int error, isroot;
544
545 if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
546 return EPROTONOSUPPORT;
547 isroot = ncp_suser(cred) == 0;
548 /*
549 * Only root can change ownership
550 */
551 if (li->owner != NCP_DEFAULT_OWNER && !isroot)
552 return EPERM;
553 if (li->group != NCP_DEFAULT_GROUP &&
554 !groupmember(li->group, cred) && !isroot)
555 return EPERM;
556 if (li->owner != NCP_DEFAULT_OWNER) {
557 owner = crget();
558 owner->cr_uid = li->owner;
559 } else {
560 owner = cred;
561 crhold(owner);
562 }
563 error = ncp_conn_alloc(td, owner, &conn);
564 if (error)
565 return (error);
566 if (error) {
567 ncp_conn_free(conn);
568 return error;
569 }
570 conn->li = *li;
571 conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ?
572 li->group : cred->cr_groups[0];
573
574 if (li->retry_count == 0)
575 conn->li.retry_count = NCP_RETRY_COUNT;
576 if (li->timeout == 0)
577 conn->li.timeout = NCP_RETRY_TIMEOUT;
578 error = ncp_reconnect(conn);
579 if (error) {
580 ncp_disconnect(conn);
581 } else {
582 *aconn=conn;
583 }
584 return error;
585}
586/*
587 * Break connection and deallocate memory
588 */
589int
590ncp_disconnect(struct ncp_conn *conn) {
591
592 if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
593 return EACCES;
594 if (conn->ref_cnt != 0) return EBUSY;
595 if (conn->flags & NCPFL_PERMANENT) return EBUSY;
596 if (ncp_conn_valid(conn)) {
597 ncp_ncp_disconnect(conn);
598 }
599 ncp_sock_disconnect(conn);
600 ncp_conn_free(conn);
601 return 0;
602}
603
604void
605ncp_check_rq(struct ncp_conn *conn){
606 return;
607 if (conn->flags & NCPFL_INTR) return;
608 /* first, check for signals */
609 if (ncp_chkintr(conn,conn->td)) {
610 conn->flags |= NCPFL_INTR;
611 }
612 return;
613}