1999-10-02 04:06:24 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1999, Boris Popov
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by Boris Popov.
|
|
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* $FreeBSD$
|
|
|
|
*
|
|
|
|
* Core of NCP protocol
|
|
|
|
*/
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "opt_ipx.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/proc.h>
|
|
|
|
#include <sys/poll.h>
|
|
|
|
#include <sys/signalvar.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
|
|
|
|
#ifdef IPX
|
|
|
|
#include <netipx/ipx.h>
|
|
|
|
#include <netipx/ipx_var.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <netncp/ncp.h>
|
|
|
|
#include <netncp/ncp_conn.h>
|
|
|
|
#include <netncp/ncp_sock.h>
|
|
|
|
#include <netncp/ncp_subr.h>
|
|
|
|
#include <netncp/ncp_ncp.h>
|
|
|
|
#include <netncp/ncp_rq.h>
|
|
|
|
#include <netncp/nwerror.h>
|
|
|
|
|
|
|
|
static int ncp_do_request(struct ncp_conn *,struct ncp_rq *rqp);
|
|
|
|
static int ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target);
|
|
|
|
static int ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options);
|
|
|
|
static void ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size);
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NCP_DATA_DEBUG
|
|
|
|
static
|
|
|
|
void m_dumpm(struct mbuf *m) {
|
|
|
|
char *p;
|
|
|
|
int len;
|
|
|
|
printf("d=");
|
|
|
|
while(m) {
|
|
|
|
p=mtod(m,char *);
|
|
|
|
len=m->m_len;
|
|
|
|
printf("(%d)",len);
|
|
|
|
while(len--){
|
|
|
|
printf("%02x ",((int)*(p++)) & 0xff);
|
|
|
|
}
|
|
|
|
m=m->m_next;
|
|
|
|
};
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif /* NCP_DATA_DEBUG */
|
|
|
|
|
|
|
|
int
|
|
|
|
ncp_chkintr(struct ncp_conn *conn, struct proc *p)
|
|
|
|
{
|
|
|
|
sigset_t tmpset;
|
|
|
|
|
|
|
|
if (p == NULL)
|
|
|
|
return 0;
|
|
|
|
tmpset = p->p_siglist;
|
|
|
|
SIGSETNAND(tmpset, p->p_sigmask);
|
|
|
|
SIGSETNAND(tmpset, p->p_sigignore);
|
|
|
|
if (SIGNOTEMPTY(p->p_siglist) && NCP_SIGMASK(tmpset))
|
|
|
|
return EINTR;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process initial NCP handshake (attach)
|
|
|
|
* NOTE: Since all functions below may change conn attributes, they
|
|
|
|
* should be called with LOCKED connection, also they use procp & ucred
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ncp_ncp_connect(struct ncp_conn *conn) {
|
|
|
|
int error;
|
|
|
|
struct ncp_rphdr *rp;
|
|
|
|
DECLARE_RQ;
|
|
|
|
|
|
|
|
conn->flags &= ~(NCPFL_INVALID | NCPFL_SIGNACTIVE | NCPFL_SIGNWANTED);
|
|
|
|
conn->seq = 0;
|
|
|
|
checkbad(ncp_rq_head(rqp,NCP_ALLOC_SLOT,0,conn->procp,conn->ucred));
|
|
|
|
error=ncp_do_request(conn,rqp);
|
|
|
|
if (!error) {
|
|
|
|
rp = mtod(rqp->rp, struct ncp_rphdr*);
|
|
|
|
conn->connid = rp->conn_low + (rp->conn_high << 8);
|
|
|
|
}
|
|
|
|
ncp_rq_done(rqp);
|
|
|
|
if (error) return error;
|
|
|
|
conn->flags |= NCPFL_ATTACHED;
|
|
|
|
|
|
|
|
error = ncp_renegotiate_connparam(conn, NCP_DEFAULT_BUFSIZE, 0);
|
|
|
|
if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
|
|
|
|
printf("Unable to negotiate requested security level\n");
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
ncp_ncp_disconnect(conn);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
#ifdef NCPBURST
|
|
|
|
ncp_burst_connect(conn);
|
|
|
|
#endif
|
|
|
|
bad:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ncp_ncp_disconnect(struct ncp_conn *conn) {
|
|
|
|
int error;
|
|
|
|
struct ncp_rqhdr *ncprq;
|
|
|
|
DECLARE_RQ;
|
|
|
|
|
|
|
|
NCPSDEBUG("for connid=%d\n",conn->nc_id);
|
|
|
|
#ifdef NCPBURST
|
|
|
|
ncp_burst_disconnect(conn);
|
|
|
|
#endif
|
|
|
|
error=ncp_rq_head(rqp,NCP_FREE_SLOT,0,conn->procp,conn->ucred);
|
|
|
|
ncprq = mtod(rqp->rq,struct ncp_rqhdr*);
|
|
|
|
error=ncp_do_request(conn,rqp);
|
|
|
|
ncp_rq_done(rqp);
|
|
|
|
ncp_conn_invalidate(conn);
|
|
|
|
ncp_sock_disconnect(conn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Make a signature for the current packet and add it at the end of the
|
|
|
|
* packet.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ncp_sign_packet(struct ncp_conn *conn, struct ncp_rq *rqp, int *size) {
|
|
|
|
u_char data[64];
|
|
|
|
|
|
|
|
bzero(data, sizeof(data));
|
|
|
|
bcopy(conn->sign_root, data, 8);
|
|
|
|
setdle(data, 8, *size);
|
|
|
|
m_copydata(rqp->rq, sizeof(struct ncp_rqhdr)-1,
|
|
|
|
min((*size) - sizeof(struct ncp_rqhdr)+1, 52),data+12);
|
|
|
|
ncp_sign(conn->sign_state, data, conn->sign_state);
|
|
|
|
ncp_rq_mem(rqp, (void*)conn->sign_state, 8);
|
|
|
|
(*size) += 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Low level send rpc, here we do not attempt to restore any connection,
|
|
|
|
* Connection expected to be locked
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ncp_do_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
|
|
|
|
int error=EIO,len, dosend, plen = 0, gotpacket, s;
|
|
|
|
struct socket *so;
|
|
|
|
struct proc *p = conn->procp;
|
|
|
|
struct ncp_rqhdr *rq;
|
|
|
|
struct ncp_rphdr *rp=NULL;
|
|
|
|
struct timeval tv;
|
|
|
|
struct mbuf *m, *mreply = NULL;
|
|
|
|
|
|
|
|
conn->nc_rq = rqp;
|
1999-10-29 10:21:07 +00:00
|
|
|
rqp->conn = conn;
|
1999-10-02 04:06:24 +00:00
|
|
|
if (p == NULL)
|
|
|
|
p = curproc; /* XXX maybe procpage ? */
|
|
|
|
if (!ncp_conn_valid(conn)) {
|
|
|
|
printf("%s: conn not valid\n",__FUNCTION__);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
so = conn->ncp_so;
|
|
|
|
if (!so) {
|
|
|
|
printf("%s: ncp_so is NULL !\n",__FUNCTION__);
|
|
|
|
ncp_conn_invalidate(conn); /* wow ! how we do that ? */
|
|
|
|
return EBADF;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Flush out replies on previous reqs
|
|
|
|
*/
|
|
|
|
s = splnet();
|
|
|
|
while (1/*so->so_rcv.sb_cc*/) {
|
|
|
|
if (ncp_poll(so,POLLIN) == 0) break;
|
|
|
|
if (ncp_sock_recv(so,&m,&len) != 0) break;
|
|
|
|
m_freem(m);
|
|
|
|
}
|
|
|
|
rq = mtod(rqp->rq,struct ncp_rqhdr *);
|
|
|
|
rq->seq = conn->seq;
|
|
|
|
m = rqp->rq;
|
|
|
|
len = 0;
|
|
|
|
while (m) {
|
|
|
|
len += m->m_len;
|
|
|
|
m = m->m_next;
|
|
|
|
}
|
|
|
|
rqp->rq->m_pkthdr.len = len;
|
|
|
|
switch(rq->fn) {
|
|
|
|
case 0x15: case 0x16: case 0x17: case 0x23:
|
|
|
|
m = rqp->rq;
|
|
|
|
*((u_int16_t*)(mtod(m,u_int8_t*)+sizeof(*rq))) = htons(len-2-sizeof(*rq));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (conn->flags & NCPFL_SIGNACTIVE) {
|
|
|
|
ncp_sign_packet(conn, rqp, &len);
|
|
|
|
rqp->rq->m_pkthdr.len = len;
|
|
|
|
}
|
|
|
|
rq->conn_low = conn->connid & 0xff;
|
|
|
|
/* rq->task = p->p_pgrp->pg_id & 0xff; */ /*p->p_pid*/
|
|
|
|
/* XXX: this is temporary fix till I find a better solution */
|
|
|
|
rq->task = rq->conn_low;
|
|
|
|
rq->conn_high = conn->connid >> 8;
|
|
|
|
rqp->rexmit = conn->li.retry_count;
|
|
|
|
for(dosend = 1;;) {
|
|
|
|
if (rqp->rexmit-- == 0) {
|
|
|
|
error = ETIMEDOUT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = 0;
|
|
|
|
if (dosend) {
|
|
|
|
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,
|
|
|
|
rqp->rq->m_pkthdr.len, rq->seq, rq->task
|
|
|
|
);
|
|
|
|
error = ncp_sock_send(so, rqp->rq, rqp);
|
|
|
|
if (error) break;
|
|
|
|
}
|
|
|
|
tv.tv_sec = conn->li.timeout;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
error = ncp_sock_rselect(so, p, &tv, POLLIN);
|
|
|
|
if (error == EWOULDBLOCK ) /* timeout expired */
|
|
|
|
continue;
|
|
|
|
error = ncp_chkintr(conn, p);
|
|
|
|
if (error == EINTR) /* we dont restart */
|
|
|
|
break;
|
|
|
|
if (error) break;
|
|
|
|
/*
|
|
|
|
* At this point it is possible to get more than one
|
|
|
|
* reply from server. In general, last reply should be for
|
|
|
|
* current request, but not always. So, we loop through
|
|
|
|
* all replies to find the right answer and flush others.
|
|
|
|
*/
|
|
|
|
gotpacket = 0; /* nothing good found */
|
|
|
|
dosend = 1; /* resend rq if error */
|
|
|
|
for (;;) {
|
|
|
|
error = 0;
|
|
|
|
if (ncp_poll(so,POLLIN) == 0) break;
|
|
|
|
/* if (so->so_rcv.sb_cc == 0) {
|
|
|
|
break;
|
|
|
|
}*/
|
|
|
|
error = ncp_sock_recv(so,&m,&len);
|
|
|
|
if (error) break; /* must be more checks !!! */
|
|
|
|
if (m->m_len < sizeof(*rp)) {
|
|
|
|
m = m_pullup(m, sizeof(*rp));
|
|
|
|
if (m == NULL) {
|
|
|
|
printf("%s: reply too short\n",__FUNCTION__);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rp = mtod(m, struct ncp_rphdr*);
|
|
|
|
if (len == sizeof(*rp) && rp->type == NCP_POSITIVE_ACK) {
|
|
|
|
NCPSDEBUG("got positive acknowledge\n");
|
|
|
|
m_freem(m);
|
|
|
|
rqp->rexmit = conn->li.retry_count;
|
|
|
|
dosend = 0; /* server just busy and will reply ASAP */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
NCPSDEBUG("recv:%04x c=%d l=%d s=%d t=%d cc=%02x cs=%02x\n",rp->type,
|
|
|
|
(rp->conn_high << 8) + rp->conn_low, len, rp->seq, rp->task,
|
|
|
|
rp->completion_code, rp->connection_state);
|
|
|
|
NCPDDEBUG(m);
|
|
|
|
if ( (rp->type == NCP_REPLY) &&
|
|
|
|
((rq->type == NCP_ALLOC_SLOT) ||
|
|
|
|
((rp->conn_low == rq->conn_low) &&
|
|
|
|
(rp->conn_high == rq->conn_high)
|
|
|
|
))) {
|
|
|
|
if (rq->seq > rp->seq || (rq->seq == 0 && rp->seq == 0xff)) {
|
|
|
|
dosend = 1;
|
|
|
|
}
|
|
|
|
if (rp->seq == rq->seq) {
|
|
|
|
if (gotpacket) {
|
|
|
|
m_freem(m);
|
|
|
|
} else {
|
|
|
|
gotpacket = 1;
|
|
|
|
mreply = m;
|
|
|
|
plen = len;
|
|
|
|
}
|
|
|
|
continue; /* look up other for other packets */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_freem(m);
|
|
|
|
NCPSDEBUG("reply mismatch\n");
|
|
|
|
} /* for receive */
|
|
|
|
if (error) break;
|
|
|
|
if (gotpacket) break;
|
|
|
|
/* try to resend, or just wait */
|
|
|
|
}
|
|
|
|
splx(s);
|
|
|
|
conn->seq++;
|
|
|
|
if (error) {
|
|
|
|
NCPSDEBUG("error=%d\n",error);
|
|
|
|
if (error != EINTR) /* if not just interrupt */
|
|
|
|
ncp_conn_invalidate(conn); /* only reconnect to restore */
|
|
|
|
return(error);
|
|
|
|
}
|
|
|
|
if (conn->flags & NCPFL_SIGNACTIVE) {
|
|
|
|
/* XXX: check reply signature */
|
|
|
|
m_adj(mreply, -8);
|
|
|
|
plen -= 8;
|
|
|
|
}
|
|
|
|
len = plen;
|
|
|
|
m = mreply;
|
|
|
|
rp = mtod(m, struct ncp_rphdr*);
|
|
|
|
len -= sizeof(*rp);
|
|
|
|
rqp->rpsize = len;
|
|
|
|
rqp->cc = error = rp->completion_code;
|
|
|
|
if (error) error |= 0x8900; /* server error */
|
|
|
|
rqp->cs = rp->connection_state;
|
|
|
|
if (rqp->cs & (NCP_CS_BAD_CONN | NCP_CS_SERVER_DOWN)) {
|
|
|
|
NCPSDEBUG("server drop us\n");
|
|
|
|
ncp_conn_invalidate(conn);
|
|
|
|
error = ECONNRESET;
|
|
|
|
}
|
|
|
|
rqp->rp = m;
|
|
|
|
rqp->mrp = m;
|
|
|
|
rqp->bpos = mtod(m, caddr_t) + sizeof(*rp);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we will try to restore any loggedin & dropped connection,
|
|
|
|
* connection should be locked on entry
|
|
|
|
*/
|
|
|
|
int ncp_restore_login(struct ncp_conn *conn);
|
|
|
|
int
|
|
|
|
ncp_restore_login(struct ncp_conn *conn) {
|
|
|
|
int error, oldflags;
|
|
|
|
|
|
|
|
if (conn->flags & NCPFL_RESTORING) {
|
|
|
|
printf("Hey, ncp_restore_login called twise !!!\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
oldflags = conn->flags;
|
|
|
|
printf("Restoring connection, flags = %d\n",oldflags);
|
|
|
|
if ((oldflags & NCPFL_LOGGED) == 0) {
|
|
|
|
return ECONNRESET; /* no need to restore empty conn */
|
|
|
|
}
|
|
|
|
conn->flags &= ~(NCPFL_LOGGED | NCPFL_ATTACHED);
|
|
|
|
conn->flags |= NCPFL_RESTORING;
|
|
|
|
do { /* not a loop */
|
|
|
|
error = ncp_reconnect(conn);
|
|
|
|
if (error) break;
|
|
|
|
if (conn->li.user)
|
|
|
|
error = ncp_login_object(conn, conn->li.user, conn->li.objtype, conn->li.password,conn->procp,conn->ucred);
|
|
|
|
if (error) break;
|
|
|
|
conn->flags |= NCPFL_LOGGED;
|
|
|
|
} while(0);
|
|
|
|
if (error) {
|
|
|
|
conn->flags = oldflags | NCPFL_INVALID;
|
|
|
|
}
|
|
|
|
conn->flags &= ~NCPFL_RESTORING;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ncp_request(struct ncp_conn *conn, struct ncp_rq *rqp) {
|
|
|
|
int error, rcnt;
|
|
|
|
/* struct ncp_rqhdr *rq = mtod(rqp->rq,struct ncp_rqhdr*);*/
|
|
|
|
|
|
|
|
error = ncp_conn_lock(conn,rqp->p,rqp->cred,NCPM_EXECUTE);
|
|
|
|
if (error) return error;
|
|
|
|
rcnt = NCP_RESTORE_COUNT;
|
|
|
|
for(;;) {
|
|
|
|
if (!ncp_conn_valid(conn)) {
|
|
|
|
if (rcnt==0) {
|
|
|
|
error = ECONNRESET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rcnt--;
|
|
|
|
error = ncp_restore_login(conn);
|
|
|
|
if (error)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
error=ncp_do_request(conn, rqp);
|
|
|
|
if (ncp_conn_valid(conn)) /* not just error ! */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ncp_conn_unlock(conn,rqp->p);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All negotiation functions expect a locked connection
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ncp_negotiate_buffersize(struct ncp_conn *conn, int size, int *target) {
|
|
|
|
int error;
|
|
|
|
DECLARE_RQ;
|
|
|
|
|
|
|
|
NCP_RQ_HEAD(0x21,conn->procp,conn->ucred);
|
|
|
|
ncp_rq_word_hl(rqp, size);
|
|
|
|
checkbad(ncp_request(conn,rqp));
|
|
|
|
*target = min(ncp_rp_word_hl(rqp), size);
|
|
|
|
NCP_RQ_EXIT;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ncp_negotiate_size_and_options(struct ncp_conn *conn, int size, int options,
|
|
|
|
int *ret_size, int *ret_options) {
|
|
|
|
int error;
|
|
|
|
int rs;
|
|
|
|
DECLARE_RQ;
|
|
|
|
|
|
|
|
NCP_RQ_HEAD(0x61,conn->procp,conn->ucred);
|
|
|
|
ncp_rq_word_hl(rqp, size);
|
|
|
|
ncp_rq_byte(rqp, options);
|
|
|
|
checkbad(ncp_request(conn, rqp));
|
|
|
|
rs = ncp_rp_word_hl(rqp);
|
|
|
|
*ret_size = (rs == 0) ? size : min(rs, size);
|
|
|
|
ncp_rp_word_hl(rqp); /* skip echo socket */
|
|
|
|
*ret_options = ncp_rp_byte(rqp);
|
|
|
|
NCP_RQ_EXIT;
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int in_options)
|
|
|
|
{
|
|
|
|
int neg_buffsize, error, options, sl;
|
|
|
|
|
|
|
|
sl = conn->li.sig_level;
|
|
|
|
if (sl >= 2)
|
|
|
|
in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
|
|
|
|
#ifdef IPX
|
|
|
|
if (ipxcksum == 2)
|
|
|
|
in_options |= NCP_IPX_CHECKSUM;
|
|
|
|
#endif
|
|
|
|
error = ncp_negotiate_size_and_options(conn, buffsize, in_options,
|
|
|
|
&neg_buffsize, &options);
|
|
|
|
if (!error) {
|
|
|
|
#ifdef IPX
|
|
|
|
if ((options ^ in_options) & NCP_IPX_CHECKSUM) {
|
|
|
|
if (ipxcksum == 2) {
|
|
|
|
printf("Server refuses to support IPX checksums\n");
|
|
|
|
return NWE_REQUESTER_FAILURE;
|
|
|
|
}
|
|
|
|
in_options |= NCP_IPX_CHECKSUM;
|
|
|
|
error = 1;
|
|
|
|
}
|
|
|
|
#endif /* IPX */
|
|
|
|
if ((options ^ in_options) & 2) {
|
|
|
|
if (sl == 0 || sl == 3)
|
|
|
|
return NWE_SIGNATURE_LEVEL_CONFLICT;
|
|
|
|
if (sl == 1) {
|
|
|
|
in_options |= NCP_SECURITY_LEVEL_SIGN_HEADERS;
|
|
|
|
error = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
error = ncp_negotiate_size_and_options(conn,
|
|
|
|
buffsize, in_options, &neg_buffsize, &options);
|
|
|
|
if ((options ^ in_options) & 3) {
|
|
|
|
return NWE_SIGNATURE_LEVEL_CONFLICT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
in_options &= ~NCP_SECURITY_LEVEL_SIGN_HEADERS;
|
|
|
|
error = ncp_negotiate_buffersize(conn, NCP_DEFAULT_BUFSIZE,
|
|
|
|
&neg_buffsize);
|
|
|
|
}
|
|
|
|
if (error) return error;
|
|
|
|
if ((neg_buffsize < 512) || (neg_buffsize > NCP_MAX_BUFSIZE))
|
|
|
|
return EINVAL;
|
|
|
|
conn->buffer_size = neg_buffsize;
|
|
|
|
if (in_options & NCP_SECURITY_LEVEL_SIGN_HEADERS)
|
|
|
|
conn->flags |= NCPFL_SIGNWANTED;
|
|
|
|
#ifdef IPX
|
|
|
|
ncp_sock_checksum(conn, in_options & NCP_IPX_CHECKSUM);
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
ncp_reconnect(struct ncp_conn *conn) {
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/* close any open sockets */
|
|
|
|
ncp_sock_disconnect(conn);
|
|
|
|
switch( conn->li.saddr.sa_family ) {
|
|
|
|
#ifdef IPX
|
|
|
|
case AF_IPX:
|
|
|
|
error = ncp_sock_connect_ipx(conn);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
error = ncp_sock_connect_in(conn);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
return EPROTONOSUPPORT;
|
|
|
|
}
|
|
|
|
if (!error)
|
|
|
|
error = ncp_ncp_connect(conn);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create conn structure and try to do low level connect
|
|
|
|
* Server addr should be filled in.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ncp_connect(struct ncp_conn_args *li, struct proc *p, struct ucred *cred,
|
|
|
|
struct ncp_conn **aconn)
|
|
|
|
{
|
|
|
|
struct ncp_conn *conn;
|
|
|
|
struct ucred *owner;
|
|
|
|
int error, isroot;
|
|
|
|
|
|
|
|
if (li->saddr.sa_family != AF_INET && li->saddr.sa_family != AF_IPX)
|
|
|
|
return EPROTONOSUPPORT;
|
|
|
|
isroot = ncp_suser(cred) == 0;
|
|
|
|
/*
|
|
|
|
* Only root can change ownership
|
|
|
|
*/
|
|
|
|
if (li->owner != NCP_DEFAULT_OWNER && !isroot)
|
|
|
|
return EPERM;
|
|
|
|
if (li->group != NCP_DEFAULT_GROUP &&
|
|
|
|
!groupmember(li->group, cred) && !isroot)
|
|
|
|
return EPERM;
|
|
|
|
if (li->owner != NCP_DEFAULT_OWNER) {
|
|
|
|
owner = crget();
|
|
|
|
owner->cr_uid = li->owner;
|
|
|
|
} else {
|
|
|
|
owner = cred;
|
|
|
|
crhold(owner);
|
|
|
|
}
|
|
|
|
error = ncp_conn_alloc(p, owner, &conn);
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
if (error) {
|
|
|
|
ncp_conn_free(conn);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
conn->li = *li;
|
|
|
|
conn->nc_group = (li->group != NCP_DEFAULT_GROUP) ?
|
|
|
|
li->group : cred->cr_groups[0];
|
|
|
|
|
|
|
|
if (li->retry_count == 0)
|
|
|
|
conn->li.retry_count = NCP_RETRY_COUNT;
|
|
|
|
if (li->timeout == 0)
|
|
|
|
conn->li.timeout = NCP_RETRY_TIMEOUT;
|
|
|
|
error = ncp_reconnect(conn);
|
|
|
|
if (error) {
|
|
|
|
ncp_disconnect(conn);
|
|
|
|
} else {
|
|
|
|
*aconn=conn;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Break connection and deallocate memory
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ncp_disconnect(struct ncp_conn *conn) {
|
|
|
|
|
|
|
|
if (ncp_conn_access(conn,conn->ucred,NCPM_WRITE))
|
|
|
|
return EACCES;
|
|
|
|
if (conn->ref_cnt != 0) return EBUSY;
|
|
|
|
if (conn->flags & NCPFL_PERMANENT) return EBUSY;
|
|
|
|
if (ncp_conn_valid(conn)) {
|
|
|
|
ncp_ncp_disconnect(conn);
|
|
|
|
}
|
|
|
|
ncp_sock_disconnect(conn);
|
|
|
|
ncp_conn_free(conn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ncp_check_rq(struct ncp_conn *conn){
|
|
|
|
return;
|
|
|
|
if (conn->flags & NCPFL_INTR) return;
|
|
|
|
/* first, check for signals */
|
|
|
|
if (ncp_chkintr(conn,conn->procp)) {
|
|
|
|
conn->flags |= NCPFL_INTR;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|