PageRenderTime 64ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/sys/netproto/ncp/ncp_ncp.c

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