Files
ncpfs/lib/ncplib.c
2026-04-28 20:56:02 +02:00

4404 lines
100 KiB
C

/*
ncplib.c
Copyright (C) 1995, 1996 by Volker Lendecke
Copyright (C) 1996, 1997, 1998, 1999, 2000 Petr Vandrovec
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Revision history:
0.00 1995 Volker Lendecke
Initial release.
0.xx 1995-1999 Petr Vandrovec <vandrove@vc.cvut.cz>
Paul Rensing <paulr@dragonsys.com>
Wolfram Pienkoss <wp@bszh.de>
Eloy A. Paris <>
David Woodhouse <dave@imladris.demon.co.uk>
Christian Groessler <cpg@aladdin.de>
Martin Stover <>
Rik Faith <faith@cs.unc.edu>
Uwe Bones <bon@elektron.ikp.physik.th-darmstadt.de>
Tomasz Babczynski <faster@dino.ict.pwr.wroc.pl>
Guntram Blom <>
Brian G. Reid <breid@tim.com>
Jeff Buhrt <buhrt@iquest.net>
Neil Turton <ndt1001@chu.cam.ac.uk>
Tom C. Henderson <thenderson@tim.com>
Mathew Lim <M.Lim@sp.ac.sg>
Steven M. Hirsch <hirsch@emba.uvm.edu>
Volker Lendecke
Different contributuions. See ../Changes for details.
1.00 1999, November 20 Petr Vandrovec <vandrove@vc.cvut.cz>
Added license.
1.01 1999, December 5 Petr Vandrovec <vandrove@vc.cvut.cz>
Added ERR_RDN_TOO_LONG error description.
Removed select() call from recv path.
1.02 2000, January 13 Petr Vandrovec <vandrove@vc.cvut.cz>
Use IPX_SAP_SPECIFIC_QUERY first. Helps on misconfigured networks
when there is no server answering get nearest service requests.
1.03 2000, January 16 Petr Vandrovec <vandrove@vc.cvut.cz>
Changes for 32bit uids.
1.04 2000, January 17 Petr Vandrovec <vandrove@vc.cvut.cz>
Fix bugs in RIP route search.
1.05 2000, January 26 Petr Vandrovec <vandrove@vc.cvut.cz>
Fixed ncp_get_fs_info for 32bit uids.
1.06 2000, May 14 Petr Vandrovec <vandrove@vc.cvut.cz>
Added support for upcoming /dev/ncp device.
1.07 2000, May 24 Petr Vandrovec <vandrove@vc.cvut.cz>
Added NWLogoutFromFileServer.
1.08 2000, May 27 Petr Vandrovec <vandrove@vc.cvut.cz>
Modified ncp_open() to try server name as DNS too, so
now you can use -S dns.name instead of -A dns.name. Fixes
problem with pam_ncp_auth in pure-IP environment.
1.09 2000, Jun 20 Petr Vandrovec <vandrove@vc.cvut.cz>
Added NCP/TCP userspace support.
1.10 2000, August 25 Petr Vandrovec <vandrove@vc.cvut.cz>
Added some error descriptions.
1.11 2001, January 11 Patrick Pollet <Patrick.Pollet@cipcinsa.insa-lyon.fr>
ncp_find_conn_spec2 should use passed 'uid' instead of doing 'getuid()'
on its own. Could affect PHP ncp auth module and Apache ncp auth module.
1.12 2001, March 4 Petr Vandrovec <vandrove@vc.cvut.cz>
Changed ncp_scan_bindery_object() prototype: search_filter is const.
Added ncp_next_conn.
1.13 2001, May 31 Petr Vandrovec <vandrove@vc.cvut.cz>
Include strings.h for AIX, use waitpid instead of wait4, call
ioctl(,NCP_*,) only when compiling with kernel support.
1.14 2001, September 9 Petr Vandrovec <vandrove@vc.cvut.cz>
Fix exec_nwsfind to open child's stdin/stderr with O_RDWR
instead of O_RDONLY.
1.15 2001, September 23 Petr Vandrovec <vandrove@vc.cvut.cz>
Always initialize unused fields of struct ncp_bindery_object
to zero (in ncp_get_bindery_object_{id,name} and
ncp_get_stations_logged_info).
1.16 2001, September 23 Petr Vandrovec <vandrove@vc.cvut.cz>
Added ncp_change_object_security... How is it possible that
nobody never evere noticed that this function occurs only
in headers?
1.17 2001, December 4 Petr Vandrovec <vandrove@vc.cvut.cz>
Call waitpid() without WNOHANG. Otherwise we can leave zombies
on SMP machines, and maybe even on UP.
1.18 2002, January 6 Petr Vandrovec <vandrove@vc.cvut.cz>
Added NULL pointer checks here and there.
Added NCP reply size checks.
1.19 2003, March 19 Patrick Pollet <Patrick.Pollet@cipcinsa.insa-lyon.fr>
Modified ncp_find_permanent & ncp_find_conn_spec3 to fix
ncp_initialize not accepting -S /mount/point.
*/
/* #define CONFIG_NATIVE_UNIX */
#undef CONFIG_NATIVE_UNIX
#define NCP_OBSOLETE
#if 0
#define ncp_dprintf(X...) printf(X)
#else
#define ncp_dprintf(X...)
#endif
#include "config.h"
#include "ncplib_i.h"
/* due to NWVerifyObjectPassword... */
#include <ncp/nwcalls.h>
#include <ncp/nwnet.h>
#include "ncpsign.h"
#ifdef NDS_SUPPORT
int bindery_only = 0;
#include <ncp/ndslib.h>
#endif
#include <ncp/nwnet.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <ncp/ext/socket.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <ncp/kernel/route.h>
#include <sys/param.h>
#include <stdlib.h>
#include <sys/mman.h>
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
#include <mntent.h>
#endif
#include <pwd.h>
#include <sys/stat.h>
#include <stdarg.h>
#ifdef CONFIG_NATIVE_IP
#include <netdb.h>
#endif
#ifdef CONFIG_NATIVE_UNIX
#include <linux/un.h>
#endif
#include <sys/uio.h>
#include "private/ncp-new.h"
#include "ncpcode.h"
#include "cfgfile.h"
#include "private/libintl.h"
#define _(X) dgettext(NCPFS_PACKAGE, (X))
#define N_(X) (X)
#ifndef ENOPKG
#define ENOPKG ENOSYS
#endif
#define NCP_DEFAULT_BUFSIZE 1024
#define NCP_MAX_BUFSIZE 1024
#ifdef SIGNATURES
int in_options = 2;
#else
int in_options = 0;
#endif
static ncpt_mutex_t conn_lock = NCPT_MUTEX_INITIALIZER;
static NCP_LIST_HEAD(conn_list);
ncpt_mutex_t nds_ring_lock = NCPT_MUTEX_INITIALIZER;
/* return number of bytes in packet */
static inline size_t
ncp_packet_size(struct ncp_conn *_conn)
{
return _conn->current_point - _conn->packet - 6;
}
static long
ncp_negotiate_buffersize(struct ncp_conn *conn,
size_t size, size_t *target);
#ifdef SIGNATURES
static NWCCODE
ncp_negotiate_size_and_options(struct ncp_conn *conn,
size_t size, int options,
size_t *ret_size, int *ret_options);
#endif
static long
ncp_login_object(struct ncp_conn *conn,
const unsigned char *username,
int login_type,
const unsigned char *password);
static long
ncp_do_close(struct ncp_conn *conn);
void
str_upper(char *name)
{
while (*name) {
*name = toupper(*name);
name = name + 1;
}
}
#ifdef NCP_TRACE_ENABLE
static FILE *logfile = 0;
inline FILE *
get_logfile()
{
if (!logfile)
logfile = fopen("/var/log/ncplib.trc", "aw");
return logfile;
}
void
__ncp_trace(char *module, int line, char *p,...)
{
FILE *log = get_logfile();
va_list ap;
if (!log)
return;
fprintf(log, "%s(%d)->", module, line);
va_start(ap, p);
vfprintf(log, p, ap);
va_end(ap);
fprintf(log, "\n");
fflush(log);
}
void
__dump_hex(const char *_msg, const unsigned char *_buf, size_t _len)
{
static char sym[] = "0123456789ABCDEF";
FILE *log = get_logfile();
if (!log)
return;
fprintf(log, "len = %d:msg->%s", _len, _msg);
fflush(log);
for (; _len > 0; _len--, _buf++) {
putc(sym[(*_buf) >> 4], log);
putc(sym[(*_buf) & 0x0F], log);
}
putc('\n', log);
fflush(log);
}
#endif /*def NCP_TRACE_ENABLE */
#if 0
static int debug_level = 5;
static FILE *logfile = stderr;
static void
dprintf(int level, char *p,...)
{
va_list ap;
if (level > debug_level) {
return;
}
va_start(ap, p);
vfprintf(logfile, p, ap);
va_end(ap);
fprintf(logfile, "\n");
fflush(logfile);
}
#endif
/* I know it's terrible to include a .c file here, but I want to keep
the file nwcrypt.c intact and separate for copyright reasons */
#include "nwcrypt.c"
void
ipx_fprint_node(FILE * file, IPXNode node)
{
fprintf(file, "%02X%02X%02X%02X%02X%02X",
(unsigned char) node[0],
(unsigned char) node[1],
(unsigned char) node[2],
(unsigned char) node[3],
(unsigned char) node[4],
(unsigned char) node[5]
);
}
void
ipx_fprint_network(FILE * file, IPXNet net)
{
fprintf(file, "%08X", (u_int32_t)ntohl(net));
}
void
ipx_fprint_port(FILE * file, IPXPort port)
{
fprintf(file, "%04X", ntohs(port));
}
void
ipx_print_node(IPXNode node)
{
ipx_fprint_node(stdout, node);
}
void
ipx_print_network(IPXNet net)
{
ipx_fprint_network(stdout, net);
}
void
ipx_print_port(IPXPort port)
{
ipx_fprint_port(stdout, port);
}
int
ipx_node_equal(CIPXNode n1, CIPXNode n2)
{
return memcmp(n1, n2, IPX_NODE_LEN) == 0;
}
int
ipx_sscanf_node(char *buf, unsigned char node[6])
{
int i;
int n[6];
if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x",
&(n[0]), &(n[1]), &(n[2]),
&(n[3]), &(n[4]), &(n[5]))) != 6) {
return i;
}
for (i = 0; i < 6; i++) {
node[i] = n[i];
}
return 6;
}
#ifdef CONFIG_NATIVE_IPX
void
ipx_fprint_saddr(FILE * file, struct sockaddr_ipx *sipx)
{
ipx_fprint_network(file, sipx->sipx_network);
fprintf(file, ":");
ipx_fprint_node(file, sipx->sipx_node);
fprintf(file, ":");
ipx_fprint_port(file, sipx->sipx_port);
}
void
ipx_print_saddr(struct sockaddr_ipx *sipx)
{
ipx_fprint_saddr(stdout, sipx);
}
int
ipx_sscanf_saddr(char *buf, struct sockaddr_ipx *target)
{
char *p;
struct sockaddr_ipx addr;
unsigned long sipx_network;
addr.sipx_family = AF_IPX;
addr.sipx_type = NCP_PTYPE;
if (sscanf(buf, "%lx", &sipx_network) != 1) {
return 1;
}
addr.sipx_network = htonl(sipx_network);
if ((p = strchr(buf, ':')) == NULL) {
return 1;
}
p += 1;
if (ipx_sscanf_node(p, addr.sipx_node) != 6) {
return 1;
}
if ((p = strchr(p, ':')) == NULL) {
return 1;
}
p += 1;
if (sscanf(p, "%hx", &addr.sipx_port) != 1) {
return 1;
}
addr.sipx_port = htons(addr.sipx_port);
*target = addr;
return 0;
}
#endif /* CONFIG_NATIVE_IPX */
NWCCODE
x_recvfrom(int sock, void *buf, int len, unsigned int flags,
struct sockaddr *sender, socklen_t* addrlen, int timeout,
size_t* rlen)
{
int result;
restart:
if (timeout >= 0) {
struct pollfd pfd;
pfd.fd = sock;
pfd.events = POLLIN | POLLHUP;
if ((result = poll(&pfd, 1, timeout)) == -1) {
if (errno == EINTR) {
timeout /= 2;
goto restart;
}
return errno;
}
if (!(pfd.revents & (POLLIN | POLLHUP))) {
return ETIMEDOUT;
}
}
result = sender?recvfrom(sock, buf, len, flags,
sender, addrlen):
recv(sock, buf, len, flags);
if (result < 0)
return errno;
*rlen = result;
return 0;
}
int
ncp_fd_nosigpipe(UNUSED(int fd))
{
#ifdef HAVE_SO_NOSIGPIPE
static const int one = 1;
return setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
#else
return 0;
#endif
}
#ifdef CONFIG_NATIVE_IPX
static int
exec_nwsfind(const char* request[]) {
int err;
signal(SIGCHLD, SIG_DFL);
err = fork();
if (err < 0)
return errno;
if (err == 0) {
close(0);
close(1);
close(2);
open("/dev/null", O_RDWR); /* fd 0 */
dup2(0, 1);
dup2(0, 2);
request[0] = NWSFIND;
execv(request[0], (char**)request);
/* OK, something bad happen... Drop it to /dev/null */
exit(127);
} else {
int rd;
if (waitpid(err, &rd, 0) != err) {
return -1;
}
if (!WIFEXITED(rd)) {
return -1;
}
if (WEXITSTATUS(rd)) {
return -1;
}
}
return 0;
}
/* Used only by nwsfind */
int ipx_make_reachable_rip(const struct sockaddr_ipx* target);
int
ipx_make_reachable_rip(const struct sockaddr_ipx* target)
{
IPXNet network = target->sipx_network;
struct rtentry rt_def;
/* Router */
struct sockaddr_ipx *sr = (struct sockaddr_ipx *) &rt_def.rt_gateway;
/* Target */
struct sockaddr_ipx *st = (struct sockaddr_ipx *) &rt_def.rt_dst;
struct ipx_rip_packet rip;
struct sockaddr_ipx addr;
socklen_t addrlen;
int sock;
int opt;
int res = -1;
int i;
int packets;
memset(&rip, 0, sizeof(rip));
sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX);
if (sock == -1) {
return errno;
}
if (ncp_fd_nosigpipe(sock)) {
close(sock);
return errno;
}
opt = 1;
/* Permit broadcast output */
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &opt, sizeof(opt)) != 0) {
goto finished;
}
memset(&addr, 0, sizeof(addr));
addr.sipx_family = AF_IPX;
addr.sipx_network = htonl(0x0);
addr.sipx_port = htons(0x0);
addr.sipx_type = IPX_RIP_PTYPE;
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
goto finished;
}
addr.sipx_family = AF_IPX;
addr.sipx_port = htons(IPX_RIP_PORT);
addr.sipx_type = IPX_RIP_PTYPE;
addr.sipx_network = htonl(0x0);
ipx_assign_node(addr.sipx_node, IPX_BROADCAST_NODE);
rip.operation = htons(IPX_RIP_REQUEST);
rip.rt[0].network = network;
if (sendto(sock, &rip, sizeof(rip), 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
goto finished;
}
packets = 3;
do {
NWCCODE err;
size_t len;
if (packets == 0) {
goto finished;
}
addrlen = sizeof(struct sockaddr_ipx);
err = x_recvfrom(sock, &rip, sizeof(rip), 0, (struct sockaddr*)sr, &addrlen, 1000,
&len);
if (err || len < sizeof(rip)) {
packets = packets - 1;
continue;
}
} while (ntohs(rip.operation) != IPX_RIP_RESPONSE);
if (rip.rt[0].network != network) {
goto finished;
}
rt_def.rt_flags = RTF_GATEWAY;
st->sipx_network = network;
st->sipx_family = AF_IPX;
sr->sipx_family = AF_IPX;
i = 0;
do {
res = ioctl(sock, SIOCADDRT, &rt_def);
i++;
} while ((i < 5) && (res < 0) && (errno == EAGAIN));
finished:
close(sock);
if (res) {
return ENETUNREACH;
}
return 0;
}
static int
ipx_make_reachable_call(const struct sockaddr_ipx* target)
{
char buf[40];
const char *buf2[4];
buf2[1] = "-a";
buf2[2] = buf;
buf2[3] = NULL;
sprintf(buf, "%08x:%02x%02x%02x%02x%02x%02x:%04x",
(u_int32_t)ntohl(target->sipx_network),
((const unsigned char*)target->sipx_node)[0],
((const unsigned char*)target->sipx_node)[1],
((const unsigned char*)target->sipx_node)[2],
((const unsigned char*)target->sipx_node)[3],
((const unsigned char*)target->sipx_node)[4],
((const unsigned char*)target->sipx_node)[5],
ntohs(target->sipx_port));
return exec_nwsfind(buf2) ? ENETUNREACH : 0;
}
static int ipx_make_reachable(const struct sockaddr_ipx* target) {
if (geteuid() == 0)
return ipx_make_reachable_rip(target);
else
return ipx_make_reachable_call(target);
}
void
ipx_assign_node(IPXNode dest, CIPXNode src)
{
memcpy(dest, src, IPX_NODE_LEN);
}
static void run_wdog(struct ncp_conn *conn, int fd) {
struct pollfd pfd[2];
pfd[0].fd = conn->wdog_sock;
pfd[0].events = POLLIN;
pfd[1].fd = fd;
pfd[1].events = POLLIN | POLLHUP;
while (1) {
switch (poll(pfd, 2, -1)) {
case -1:
if (errno != EINTR) {
/* Commit suicide, error happened */
return;
}
break;
case 0:
break;
default:
if (pfd[0].revents & POLLIN) {
struct sockaddr_ipx sender;
int sizeofaddr = sizeof(struct sockaddr_ipx);
char buf[1024];
size_t pktsize;
NWCCODE err;
err = x_recvfrom(pfd[0].fd, buf, sizeof(buf), 0,
(struct sockaddr*)&sender, &sizeofaddr, 120000, &pktsize);
if (!err && pktsize >= 2 && buf[1] == '?') {
buf[1] = 'Y';
pktsize = sendto(pfd[0].fd, buf, 2, 0, (struct sockaddr *)&sender, sizeof(sender));
}
}
if (pfd[0].revents & (POLLHUP | POLLNVAL)) {
/* HUP or invalid? Both unexpected => die */
return;
}
if (pfd[1].revents & POLLHUP) {
/* Parent closed NCP connection => stop */
return;
}
if (pfd[1].revents & POLLIN) {
int i;
char cmd;
/* Parent wrote something to us */
i = read(fd, &cmd, 1);
if (i == 1) {
switch (cmd) {
case 'Q': /* quit */
return;
}
}
/* Something wrong happened => die */
return;
}
if (pfd[1].revents & POLLNVAL) {
/* Parent either send us some data, or invalid => die */
return;
}
break;
}
}
}
static int
install_wdog(struct ncp_conn *conn)
{
int pid;
int fildes[2];
int i;
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fildes)) {
return -1;
}
fcntl(fildes[0], F_SETFD, FD_CLOEXEC);
fcntl(fildes[1], F_SETFD, FD_CLOEXEC);
if ((pid = fork()) < 0) {
close(fildes[0]);
close(fildes[1]);
return -1;
}
if (pid != 0) {
int status;
pid_t p;
close(fildes[0]);
/* Parent: wait for child to exit */
p = waitpid(pid, &status, 0);
if (p < 0) {
close(fildes[1]);
return -1;
}
if (!WIFEXITED(status) || WEXITSTATUS(status)) {
close(fildes[1]);
return -1;
}
conn->wdog_pipe = fildes[1];
return 0;
}
for (i = 0; i < 1024; i++) {
if (i == fildes[0] || i == conn->wdog_sock) {
continue;
}
close(i);
}
if ((pid = fork()) < 0) {
close(fildes[0]);
exit(1);
}
if (pid != 0) {
close(fildes[0]);
exit(0);
}
chdir("/");
run_wdog(conn, fildes[0]);
/* wdog socket must be closed before wdog pipe */
close(conn->wdog_sock);
close(fildes[0]);
exit(0);
}
#endif /* CONFIG_NATIVE_IPX */
void
ncp_lock_conn(struct ncp_conn *conn)
{
ncpt_mutex_lock(&conn->buffer_mutex);
conn->lock++;
}
void
ncp_unlock_conn(struct ncp_conn *conn)
{
assert_conn_locked(conn);
conn->lock--;
ncpt_mutex_unlock(&conn->buffer_mutex);
}
static struct ncp_conn *ncp_alloc_conn(void) {
struct ncp_conn* conn;
conn = (struct ncp_conn*)malloc(sizeof(*conn));
if (conn) {
memset(conn, 0, sizeof(*conn));
ncpt_atomic_set(&conn->use_count, 1);
ncpt_atomic_set(&conn->store_count, 0);
NCP_INIT_LIST_HEAD(&conn->nds_ring);
// conn->nds_conn = NULL;
NCP_INIT_LIST_HEAD(&conn->conn_ring);
ncpt_mutex_init(&conn->buffer_mutex);
ncpt_mutex_init(&conn->serverInfo.mutex);
// conn->serverInfo.valid = 0;
// conn->connState = 0;
conn->bcast_state = NWCC_BCAST_PERMIT_UNKNOWN;
conn->global_fd = -1;
conn->wdog_pipe = -1;
ncpt_mutex_lock(&conn_lock);
list_add(&conn->conn_ring, &conn_list);
ncpt_mutex_unlock(&conn_lock);
}
return conn;
}
static inline unsigned int get_conn_from_reply(const void* hdr) {
return BVAL(hdr, 3) | (BVAL(hdr, 5) << 8);
}
static NWCCODE
do_ncp_call(struct ncp_conn *conn, unsigned int cmd, unsigned int task, const unsigned char* request, size_t request_size)
{
int result;
int retries = 20;
size_t len;
NWCCODE err;
unsigned char header[6];
unsigned char signature[8];
struct iovec io[3];
struct msghdr msg;
conn->sequence++;
WSET_HL(header, 0, cmd);
BSET(header, 2, conn->sequence);
BSET(header, 3, conn->i.connection);
BSET(header, 4, task);
BSET(header, 5, conn->i.connection >> 8);
io[0].iov_base = header;
io[0].iov_len = 6;
io[1].iov_base = ncp_const_cast(request);
io[1].iov_len = request_size;
io[2].iov_base = signature;
io[2].iov_len = sign_packet(conn, request, request_size,
cpu_to_le32(request_size + 6), signature);
msg.msg_namelen = 0;
msg.msg_name = NULL;
msg.msg_iov = io;
msg.msg_iovlen = io[2].iov_len ? 3 : 2;
msg.msg_control = NULL;
msg.msg_controllen = 0;
conn->ncp_reply = conn->ncp_reply_buffer;
while (retries > 0) {
retries -= 1;
result = sendmsg(conn->ncp_sock, &msg, 0);
if (result < 0) {
return errno;
}
re_select:
err = x_recv(conn->ncp_sock,
conn->ncp_reply, conn->ncp_reply_alloc,
0, 3000, &len);
if (err) {
if (err == ETIMEDOUT)
continue;
return err;
}
if (len < sizeof(struct ncp_reply_header*) || len > conn->ncp_reply_alloc) {
continue;
}
if (WVAL_HL(conn->ncp_reply, 0) != NCP_REPLY) {
goto re_select;
}
if (cmd != NCP_ALLOC_SLOT_REQUEST &&
(BVAL(conn->ncp_reply, 2) != conn->sequence
|| get_conn_from_reply(conn->ncp_reply) != conn->i.connection))
{
goto re_select;
}
if (ncp_get_sign_active(conn) && cmd != NCP_DEALLOC_SLOT_REQUEST) {
unsigned int hdrl;
len -= 8;
hdrl = conn->nt == NT_UDP ? 8 : 6;
if (len < hdrl)
goto re_select;
if (sign_verify_reply(conn, conn->ncp_reply + hdrl, len - hdrl, cpu_to_le32(len), conn->ncp_reply + len)) {
goto re_select;
}
}
conn->ncp_reply_size = len - sizeof(struct ncp_reply_header);
return 0;
}
return ETIMEDOUT;
}
static int do_tcp_rcv(int fd, unsigned char* data, size_t len) {
while (len) {
int rcv;
rcv = recv(fd, data, len, 0);
if (rcv < 0) {
if (errno == EINTR)
continue;
return errno;
}
if (rcv == 0) {
return ECONNABORTED;
}
if ((size_t)rcv > len) {
return ECONNABORTED;
}
len -= rcv;
data += rcv;
}
return 0;
}
static int do_tcp_rcv_skip(int fd, size_t len) {
while (len) {
int rcv;
unsigned char dummy[1000];
if (len > sizeof(dummy)) {
rcv = recv(fd, dummy, sizeof(dummy), 0);
} else {
rcv = recv(fd, dummy, len, 0);
}
if (rcv < 0) {
if (errno == EINTR)
continue;
return errno;
}
if (rcv == 0) {
return ECONNABORTED;
}
if ((size_t)rcv > len) {
return ECONNABORTED;
}
len -= rcv;
}
return 0;
}
static NWCCODE
do_ncp_tcp_call(struct ncp_conn *conn, unsigned int cmd, unsigned int task, const unsigned char *request, size_t request_size)
{
unsigned char replyhdr[18];
int result;
unsigned char rqhdr[30];
struct iovec io[2];
struct msghdr msg;
size_t replyhdrlen;
size_t ln;
struct ncp_reply_header *reply;
size_t siglen;
conn->sequence++;
siglen = sign_packet(conn, request, request_size,
cpu_to_be32(request_size + 30), rqhdr + 16);
DSET_HL(rqhdr, 0, 0x446D6454);
DSET_HL(rqhdr, 4, request_size + 16 + siglen + 6);
DSET_HL(rqhdr, 8, 1);
DSET_HL(rqhdr, 12, sizeof(conn->packet));
WSET_HL(rqhdr, 16 + siglen + 0, cmd);
BSET(rqhdr, 16 + siglen + 2, conn->sequence);
BSET(rqhdr, 16 + siglen + 3, conn->i.connection);
BSET(rqhdr, 16 + siglen + 4, task);
BSET(rqhdr, 16 + siglen + 5, conn->i.connection >> 8);
io[0].iov_base = rqhdr;
io[0].iov_len = 16 + siglen + 6;
io[1].iov_base = ncp_const_cast(request);
io[1].iov_len = request_size;
msg.msg_namelen = 0;
msg.msg_name = NULL;
msg.msg_iov = io;
msg.msg_iovlen = 2;
msg.msg_control = NULL;
msg.msg_controllen = 0;
result = sendmsg(conn->ncp_sock, &msg, MSG_NOSIGNAL);
if (result < 0) {
return errno;
}
if ((size_t)result != io[1].iov_len + io[0].iov_len) {
return ECONNABORTED;
}
replyhdrlen = 8 + siglen + 2;
while (1) {
result = do_tcp_rcv(conn->ncp_sock, replyhdr, replyhdrlen);
if (result) {
return result;
}
if (DVAL_HL(replyhdr, 0) != 0x744E6350) {
fprintf(stderr, "RecvUnk: %08X, %08X\n", DVAL_HL(replyhdr, 0), DVAL_HL(replyhdr, 4));
return ECONNABORTED;
}
ln = DVAL_HL(replyhdr, 4) & 0x0FFFFFFF;
if (ln < replyhdrlen) {
return ECONNABORTED;
}
ln -= replyhdrlen;
if (WVAL_HL(replyhdr, replyhdrlen - 2) == NCP_REPLY) {
break;
}
result = do_tcp_rcv_skip(conn->ncp_sock, ln);
if (result) {
return result;
}
};
if (ln < sizeof(*reply) - 2) {
return ECONNABORTED;
}
if (ln > sizeof(conn->packet) - 2) {
fprintf(stderr, "Too long reply: %u\n", ln);
return ECONNABORTED;
}
result = do_tcp_rcv(conn->ncp_sock, conn->packet + 2, ln);
if (result) {
return result;
}
reply = (struct ncp_reply_header*)conn->packet;
reply->type = htons(NCP_REPLY);
if (cmd != NCP_ALLOC_SLOT_REQUEST) {
if (reply->sequence != conn->sequence
|| get_conn_from_reply(reply) != conn->i.connection) {
/* Protocol violation... */
return ECONNABORTED;
}
}
ln += 2;
if (ncp_get_sign_active(conn) && cmd != NCP_DEALLOC_SLOT_REQUEST) {
if (sign_verify_reply(conn, conn->packet + 6, ln - 6, cpu_to_be32(ln + 16), replyhdr + 8)) {
return ERR_INVALID_SIGNATURE;
}
}
conn->ncp_reply_size = ln - sizeof(struct ncp_reply_header);
conn->ncp_reply = conn->packet;
return 0;
}
static inline void
fill_size(struct ncp_conn *_conn)
{
if (_conn->has_subfunction != 0) {
size_t sz = ncp_packet_size(_conn) - 2 - 1;
WSET_HL(_conn->packet, 7, sz);
}
}
static NWCCODE
ncp_mount_request(struct ncp_conn *conn, int function)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
struct ncp_ioctl_request request;
int result;
assert_conn_locked(conn);
fill_size(conn);
request.function = function;
request.size = ncp_packet_size(conn) + 6;
request.data = conn->packet;
if ((result = ioctl(conn->mount_fid, NCP_IOC_NCPREQUEST, &request)) < 0) {
return errno;
}
conn->ncp_reply_size = result - sizeof(struct ncp_reply_header);
conn->ncp_reply = conn->packet;
result = BVAL(conn->packet, 6);
conn->conn_status = BVAL(conn->packet, 7);
if (result && (conn->verbose != 0)) {
ncp_printf(_("ncp_request_error: %d\n"), result);
}
return (result == 0) ? 0 : (NWE_SERVER_ERROR | result);
#else
return ENOPKG;
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
static NWCCODE
ncp_kernel_request(struct ncp_conn *conn, int function)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
struct ncp_ioc_request_reply request;
int result;
assert_conn_locked(conn);
fill_size(conn);
request.function = function;
request.request.size = ncp_packet_size(conn) - 1;
request.request.addr = conn->packet + sizeof(struct ncp_request_header);
request.reply.size = sizeof(conn->packet) - sizeof(struct ncp_reply_header);
request.reply.addr = conn->packet + sizeof(struct ncp_reply_header);
if ((result = ioctl(conn->mount_fid, NCP_IOC_REQUEST_REPLY, &request)) < 0) {
return errno;
}
conn->ncp_reply_size = request.reply.size - sizeof(struct ncp_reply_header);
conn->ncp_reply = conn->packet;
if (result && (conn->verbose != 0)) {
ncp_printf(_("ncp_request_error: %d\n"), result);
}
return (result == 0) ? 0 : (NWE_SERVER_ERROR | result);
#else
return ENOPKG;
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
static NWCCODE
ncp_temp_request(struct ncp_conn *conn, int function)
{
NWCCODE err;
assert_conn_locked(conn);
BSET(conn->packet, 6, function);
fill_size(conn);
switch (conn->nt) {
case NT_IPX:
case NT_UDP:
err = do_ncp_call(conn, NCP_REQUEST, 1, conn->packet + 6, ncp_packet_size(conn));
break;
case NT_TCP:
err = do_ncp_tcp_call(conn, NCP_REQUEST, 1, conn->packet + 6, ncp_packet_size(conn));
break;
default:
err = ECONNABORTED;
break;
}
if (err) {
return err;
}
err = BVAL(conn->ncp_reply, 6);
conn->conn_status = BVAL(conn->ncp_reply, 7);
if (err && (conn->verbose != 0)) {
ncp_printf(_("ncp_request_error: %d\n"), err);
}
return (err == 0) ? 0 : (NWE_SERVER_ERROR | err);
}
NWCCODE
ncp_renegotiate_siglevel(NWCONN_HANDLE conn, size_t buffsize, int siglevel) {
size_t neg_buffsize;
int err;
int newoptions;
int options;
if (ncp_get_sign_active(conn)) siglevel = 3;
if (siglevel < 2)
newoptions = 0;
else
newoptions = 2;
#ifdef SIGNATURES
err = ncp_negotiate_size_and_options(conn, buffsize, newoptions, &neg_buffsize, &options);
if (!err) {
if ((options & 2) != newoptions) {
if (siglevel == 3) {
return NWE_SIGNATURE_LEVEL_CONFLICT;
}
if (siglevel != 0) {
newoptions ^= 2;
err = ncp_negotiate_size_and_options(conn, buffsize, newoptions, &neg_buffsize, &options);
if (!err) {
if ((options & 2) != newoptions) {
return NWE_SIGNATURE_LEVEL_CONFLICT;
}
}
}
}
}
if (err)
#endif
{
if (siglevel == 3) {
return NWE_SIGNATURE_LEVEL_CONFLICT;
}
err = ncp_negotiate_buffersize(conn, buffsize, &neg_buffsize);
if (err) {
return err;
}
options = 0;
}
if ((neg_buffsize < 512) || (neg_buffsize > NCP_PACKET_SIZE - 40))
return NWE_REQUESTER_FAILURE;
conn->i.buffer_size = neg_buffsize;
#ifdef SIGNATURES
conn->sign_wanted = (options & 2) ? 1 : 0;
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
if (conn->is_connected == CONN_PERMANENT) {
int cursign;
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_WANTED, &cursign)) {
/* ncpfs does not support SIGN_WANTED -> current sign level = 0 */
cursign = 0;
}
if (cursign) cursign = 1;
if (cursign != conn->sign_wanted) {
int newsign = conn->sign_wanted ? -1 : 0;
err = ioctl(conn->mount_fid, NCP_IOC_SET_SIGN_WANTED, &newsign);
if (err) {
return errno;
}
}
}
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
#endif
return 0;
}
static inline NWCCODE
ncp_negotiate_siglevel(struct ncp_conn *conn, size_t buffsize, int siglevel) {
#ifdef SIGNATURES
conn->sign_active = 0;
conn->sign_wanted = 0;
#endif
return ncp_renegotiate_siglevel(conn, buffsize, siglevel);
}
long
ncp_renegotiate_connparam(struct ncp_conn *conn, int buffsize, int newoptions)
{
if (newoptions & 2)
newoptions = 2;
else
newoptions = 1;
return ncp_renegotiate_siglevel(conn, buffsize, newoptions);
}
static void
move_conn_to_kernel(UNUSED(struct ncp_conn *conn)) {
#if 0
int f;
int e;
struct ncp_ioc_newconn nc;
f = open("/dev/ncp", O_RDWR);
if (f == -1)
return;
nc.fd = conn->ncp_sock;
nc.flags = 0;
if (ncp_get_sign_wanted(conn))
nc.flags |= NCP_FLAGS_SIGN_NEGOTIATED;
nc.sequence = conn->sequence;
nc.connection = conn->i.connection;
e = ioctl(f, NCP_IOC_NEWCONN, &nc);
if (e) {
close(f);
return;
}
close(conn->ncp_sock);
conn->ncp_sock = -1;
conn->is_connected = CONN_KERNELBASED;
conn->mount_fid = f;
#endif
}
static NWCCODE ncp_finish_connect(struct ncp_conn *conn) {
NWCCODE err;
conn->sequence = 0;
conn->i.connection = get_conn_from_reply(conn->ncp_reply);
conn->is_connected = CONN_TEMPORARY;
err = ncp_negotiate_siglevel(conn, NCP_DEFAULT_BUFSIZE, in_options);
if (err) {
return err;
}
conn->bcast_state = NWCC_BCAST_PERMIT_ALL;
return 0;
}
#ifdef CONFIG_NATIVE_IPX
static long
ncp_connect_ipx_addr(struct ncp_conn *conn, const struct sockaddr_ipx *target,
int wdog_needed)
{
static const unsigned char zero = 0;
struct sockaddr_ipx addr;
socklen_t addrlen;
int ncp_sock, wdog_sock;
long err;
conn->ncp_reply_buffer = (char*)malloc(NCP_PACKET_SIZE);
if (!conn->ncp_reply_buffer) {
return ENOMEM;
}
conn->ncp_reply_alloc = NCP_PACKET_SIZE;
conn->is_connected = NOT_CONNECTED;
conn->verbose = 0;
if ((ncp_sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX)) == -1) {
return errno;
}
if (ncp_fd_nosigpipe(ncp_sock)) {
close(ncp_sock);
return errno;
}
if ((wdog_sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX)) == -1) {
close(ncp_sock);
return errno;
}
if (ncp_fd_nosigpipe(wdog_sock)) {
close(wdog_sock);
close(ncp_sock);
return errno;
}
addr.sipx_family = AF_IPX;
addr.sipx_port = htons(0x0);
addr.sipx_type = NCP_PTYPE;
addr.sipx_network = IPX_THIS_NET;
ipx_assign_node(addr.sipx_node, IPX_THIS_NODE);
addrlen = sizeof(addr);
if ((bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1)
|| (getsockname(ncp_sock, (struct sockaddr *) &addr, &addrlen) == -1)) {
int saved_errno = errno;
close(ncp_sock);
close(wdog_sock);
return saved_errno;
}
addr.sipx_port = htons(ntohs(addr.sipx_port) + 1);
if (bind(wdog_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
int saved_errno = errno;
close(ncp_sock);
close(wdog_sock);
return saved_errno;
}
conn->ncp_sock = ncp_sock;
conn->wdog_sock = wdog_sock;
conn->sequence = 0;
conn->addr.ipx = *target;
conn->nt = NT_IPX;
if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) {
int saved_errno = errno;
if ((saved_errno != ENETUNREACH) || ipx_make_reachable(target)
|| connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target))) {;
close(ncp_sock);
close(wdog_sock);
return saved_errno;
}
}
conn->i.connection = ~0;
if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) {
if ((err != ENETUNREACH)
|| (ipx_make_reachable(target) != 0)
|| ((err =
do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0)) {
close(ncp_sock);
close(wdog_sock);
return err;
}
}
if (wdog_needed != 0) {
install_wdog(conn);
}
err = ncp_finish_connect(conn);
if (err) {
return err;
}
move_conn_to_kernel(conn);
return 0;
}
#endif /* CONFIG_NATIVE_IPX */
#ifdef CONFIG_NATIVE_IP
static long
ncp_connect_in_addr(struct ncp_conn *conn, const struct sockaddr_in *target,
UNUSED(int wdog_needed))
{
static const unsigned char zero = 0;
struct sockaddr_in addr;
socklen_t addrlen;
int ncp_sock;
long err;
conn->ncp_reply_buffer = (char*)malloc(NCP_PACKET_SIZE);
if (!conn->ncp_reply_buffer) {
return ENOMEM;
}
conn->ncp_reply_alloc = NCP_PACKET_SIZE;
conn->is_connected = NOT_CONNECTED;
conn->verbose = 0;
if ((ncp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
return errno;
}
if (ncp_fd_nosigpipe(ncp_sock)) {
close(ncp_sock);
return errno;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(0x0);
addr.sin_addr.s_addr = INADDR_ANY;
addrlen = sizeof(addr);
if (bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
conn->ncp_sock = ncp_sock;
conn->wdog_sock = -1;
conn->sequence = 0;
memcpy(&conn->addr, target, sizeof(*target));
conn->nt = NT_UDP;
if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
conn->i.connection = ~0;
if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) {
/* ? request route ? */
close(ncp_sock);
return err;
}
err = ncp_finish_connect(conn);
if (err) {
return err;
}
move_conn_to_kernel(conn);
return 0;
}
static long
ncp_connect_tcp_addr(struct ncp_conn *conn, const struct sockaddr_in *target,
UNUSED(int wdog_needed))
{
static const unsigned char connreq[19] = { 1, 16, 0, 0 };
struct sockaddr_in addr;
socklen_t addrlen;
int ncp_sock;
long err;
conn->is_connected = NOT_CONNECTED;
conn->verbose = 0;
if ((ncp_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
return errno;
}
if (ncp_fd_nosigpipe(ncp_sock)) {
close(ncp_sock);
return errno;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(0x0);
addr.sin_addr.s_addr = INADDR_ANY;
addrlen = sizeof(addr);
if (bind(ncp_sock, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
conn->ncp_sock = ncp_sock;
conn->wdog_sock = -1;
conn->sequence = 0;
memcpy(&conn->addr, target, sizeof(*target));
conn->nt = NT_TCP;
if (connect(ncp_sock, (const struct sockaddr*)target, sizeof(*target)) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
conn->i.connection = ~0;
if ((err = do_ncp_tcp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, connreq, sizeof(connreq))) != 0) {
/* ? request route ? */
close(ncp_sock);
return err;
}
err = ncp_finish_connect(conn);
if (err) {
return err;
}
move_conn_to_kernel(conn);
return 0;
}
#endif /* CONFIG_NATIVE_IP */
#ifdef CONFIG_NATIVE_UNIX
static long
ncp_connect_un_addr(struct ncp_conn *conn, const struct sockaddr_un *target,
int wdog_needed)
{
static const unsigned char zero = 0;
struct sockaddr_un addr;
int fd;
int ncp_sock;
long err;
conn->is_connected = NOT_CONNECTED;
conn->verbose = 0;
if ((fd = socket(PF_UNIX, SOCK_DGRAM, 0)) == -1) {
return errno;
}
if (ncp_fd_nosigpipe(fd)) {
close(fd);
return errno;
}
addr.sun_family = AF_UNIX;
if (bind(fd, (struct sockaddr *) &addr, 2) == -1) {
int saved_errno = errno;
close(fd);
return saved_errno;
}
memcpy(addr.sun_path, "\000ncpfs", 6);
if (connect(fd, (struct sockaddr *) &addr, 8) == -1) {
int saved_errno = errno;
close(fd);
return saved_errno;
}
{
unsigned char buffer = 'Q';
if (send(fd, &buffer, 1, MSG_DONTWAIT) != 1) {
int saved_errno = errno;
close(fd);
return saved_errno;
}
}
{
unsigned char buffer[100];
struct msghdr msg;
struct cmsghdr* cmsg;
struct iovec io[1];
unsigned char ctrl[1024];
int ret;
io[0].iov_base = buffer;
io[0].iov_len = sizeof(buffer);
msg.msg_namelen = 0;
msg.msg_name = NULL;
msg.msg_iov = io;
msg.msg_iovlen = 1;
msg.msg_control = ctrl;
msg.msg_controllen = sizeof(ctrl);
ret = recvmsg(fd, &msg, 0);
if (ret < 0) {
int saved_errno = errno;
close(fd);
return saved_errno;
}
close(fd);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS
&& cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
break;
}
if (!cmsg)
return EACCES;
ncp_sock = *(int*)(CMSG_DATA(cmsg));
if (ncp_sock == -1)
return EINVAL;
}
#if 0
{
int val = 1;
if (setsockopt(ncp_sock, SOL_SOCKET, SO_PASSCRED, &val, sizeof(val)) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
}
#endif
conn->ncp_sock = ncp_sock;
conn->wdog_sock = -1;
conn->sequence = 0;
memset(&conn->addr, 0, sizeof(conn->addr));
#if 0
memcpy(addr.sun_path, "\000ncpfs", 6);
if (connect(ncp_sock, (const struct sockaddr*)&addr, 8) == -1) {
int saved_errno = errno;
close(ncp_sock);
return saved_errno;
}
#endif
conn->i.connection = ~0;
if ((err = do_ncp_call(conn, NCP_ALLOC_SLOT_REQUEST, 1, &zero, 1)) != 0) {
/* ? request route ? */
close(ncp_sock);
return err;
}
err = ncp_finish_connect(conn);
if (err) {
return err;
}
return 0;
}
#endif /* CONFIG_NATIVE_UNIX */
static long
ncp_connect_addr(struct ncp_conn *conn, const struct sockaddr *target,
int wdog_needed, nuint transport) {
switch (target->sa_family) {
#ifdef CONFIG_NATIVE_IPX
case AF_IPX:
if (transport == NT_IPX) {
return ncp_connect_ipx_addr(conn, (const struct sockaddr_ipx*)target, wdog_needed);
}
break;
#endif /* CONFIG_NATIVE_IPX */
#ifdef CONFIG_NATIVE_IP
case AF_INET:
switch (transport) {
case NT_TCP:
return ncp_connect_tcp_addr(conn, (const struct sockaddr_in*)target, wdog_needed);
case NT_UDP:
return ncp_connect_in_addr(conn, (const struct sockaddr_in*)target, wdog_needed);
}
break;
#endif /* CONFIG_NATIVE_IP */
#ifdef CONFIG_NATIVE_UNIX
case AF_UNIX:
return ncp_connect_un_addr(conn, (const struct sockaddr_un*)target, wdog_needed);
#endif /* CONFIG_NATIVE_UNIX */
default:
break;
}
return NWE_UNSUPPORTED_TRAN_TYPE;
}
static long
ncp_connect_any(NWCONN_HANDLE *conn, UNUSED(int wdog_needed))
{
return NWCCOpenConnByName(NULL, NULL, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn);
}
long
ncp_find_fileserver(const char *server_name, struct sockaddr* addr, socklen_t len) {
return ncp_find_server(&server_name, OT_FILE_SERVER, addr, len);
}
#ifdef NDS_SUPPORT
static NWCCODE
ncp_login_nds(struct ncp_conn* conn, const char* object_name, const char* password) {
NWCCODE err;
err = NWE_NCP_NOT_SUPPORTED;
if (NWIsDSServer(conn, NULL)) {
err = nds_login_auth(conn, object_name, password);
if (!err) return 0;
if (err == NWE_PASSWORD_EXPIRED) {
fprintf(stderr, _("Your password has expired\n"));
return 0;
}
}
return err;
}
#endif
NWCCODE
ncp_login_conn(struct ncp_conn* conn, const char* object_name, NWObjectType object_type, const char* password) {
NWCCODE err;
char* auth;
auth = cfgGetItem("Requester", "NetWare Protocol");
if (auth) {
char* ptr = auth;
char* curr;
err = NWE_UNSUPPORTED_AUTHENTICATOR;
while ((curr = strsep(&ptr, " \t,")) != NULL) {
if (!strcasecmp(curr, "BIND")) {
err = ncp_login_object(conn, object_name, object_type, password);
#ifdef NDS_SUPPORT
} else if (!strcasecmp(curr, "NDS")) {
err = ncp_login_nds(conn, object_name, password);
#endif
} else {
/* Leave error code as is... */
}
if (!err) {
break;
}
}
free(auth);
} else {
#ifdef NDS_SUPPORT
err = ncp_login_nds(conn, object_name, password);
if (!err) {
return 0;
}
#endif
err = ncp_login_object(conn, object_name, object_type, password);
}
return err;
}
static long
ncp_open_temporary(NWCONN_HANDLE *conn, const char* server) {
return NWCCOpenConnByName(NULL, server, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn);
}
static long
ncp_open_temporary2(struct ncp_conn **conn,
const char* address) {
/* Ask only for IPv4 name resolve... */
return NWCCOpenConnByName(NULL, address, NWCC_NAME_FORMAT_BIND, 0, NWCC_RESERVED, conn);
}
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
static int ncp_get_fs_info(int fd, struct ncp_fs_info_v2* info) {
int err;
struct ncp_fs_info i;
info->version = NCP_GET_FS_INFO_VERSION_V2;
err = ioctl(fd, NCP_IOC_GET_FS_INFO_V2, info);
if (err != -1) {
/* Kernel uses dirEntNum as seen by protocol (as an opaque 4 byte entity),
while userspace uses host byteorder */
info->directory_id = DVAL_LH(&info->directory_id, 0);
return err;
}
err = errno;
if (err != EINVAL)
return err;
i.version = NCP_GET_FS_INFO_VERSION;
err = ioctl(fd, NCP_IOC_GET_FS_INFO, &i);
if (err != -1) {
info->version = NCP_GET_FS_INFO_VERSION_V2;
info->mounted_uid = i.mounted_uid;
info->connection = i.connection;
info->buffer_size = i.buffer_size;
info->volume_number = i.volume_number;
/* Kernel uses dirEntNum as seen by protocol (as an opaque 4 byte entity),
while userspace uses host byteorder */
info->directory_id = DVAL_LH(&i.directory_id, 0);
info->dummy1 = info->dummy2 = info->dummy3 = 0;
return err;
}
return errno;
}
#endif
char *
ncp_find_permanent(const struct ncp_conn_spec *spec)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
FILE *mtab;
struct ncp_conn_ent *conn_ent;
char *result = NULL;
int mount_fid;
struct ncp_fs_info_v2 i;
if ((mtab = fopen(MOUNTED, "r")) == NULL) {
return NULL;
}
while ((conn_ent = ncp_get_conn_ent(mtab)) != NULL) {
if (spec != NULL) {
if (conn_ent->uid != spec->uid) {
continue;
}
if (spec->server[0] == '/') {
if (strcmp(conn_ent->mount_point, spec->server)) {
continue;
}
} else {
if (spec->server[0] &&
strcasecmp(conn_ent->server, spec->server)) {
continue;
}
if (spec->user[0] &&
strcasecmp(conn_ent->user, spec->user)) {
continue;
}
}
}
mount_fid = open(conn_ent->mount_point, O_RDONLY, 0);
if (mount_fid < 0) {
continue;
}
if (ncp_get_fs_info(mount_fid, &i)) {
close(mount_fid);
continue;
}
close(mount_fid);
result = conn_ent->mount_point;
break;
}
fclose(mtab);
errno = (result == NULL) ? ENOENT : 0;
return result;
#else
return NULL;
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
#ifdef SIGNATURES
static void
ncp_sign_init_perm(struct ncp_conn *conn)
{
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_WANTED,
&conn->sign_wanted) != 0)
conn->sign_wanted = 0;
conn->sign_active = 0;
}
#endif
#endif
static int
ncp_open_permanent(const struct ncp_conn_spec *spec, struct ncp_conn** conn)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
char *mount_point;
if ((mount_point = ncp_find_permanent(spec)) == NULL) {
return -1;
}
return ncp_open_mount(mount_point, conn);
#else
return -1;
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
static NWCCODE
ncp_open_2(struct ncp_conn **conn, const struct ncp_conn_spec *spec, const char* address)
{
struct ncp_conn *result;
static int ncp_domain_bound = 0;
NWCCODE err;
if (!ncp_domain_bound) {
bindtextdomain(NCPFS_PACKAGE, LOCALEDIR);
ncp_domain_bound = 1;
}
if (ncp_open_permanent(spec, conn) == 0) {
return 0;
}
if (spec) {
err = NWE_REQUESTER_FAILURE;
if (address) {
err = ncp_open_temporary2(&result, address);
}
if (err)
err = ncp_open_temporary(&result, spec->server);
} else {
err = ncp_connect_any(&result, 1);
}
if (err) {
return err;
}
if (spec && (strlen(spec->user) != 0)) {
err = ncp_login_conn(result, spec->user, spec->login_type, spec->password);
if (err) {
ncp_close(result);
return err;
}
result->user = strdup(spec->user);
}
*conn = result;
return 0;
}
struct ncp_conn *
ncp_open(const struct ncp_conn_spec *spec, long *err) {
NWCCODE nwerr;
struct ncp_conn* conn;
nwerr = ncp_open_2(&conn, spec, NULL);
*err = nwerr;
if (nwerr)
return NULL;
return conn;
}
static int ncp_do_open_fd(int fd, struct ncp_conn** conn) {
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
struct ncp_conn *result;
size_t klen;
*conn = NULL;
result = ncp_alloc_conn();
if (result == NULL) {
return ENOMEM;
}
result->mount_fid = fd;
result->is_connected = CONN_PERMANENT;
/* on kernels <= 2.2.10 & 2.3.8 directory_id can be set incorrectly */
/* do stat(mount_point) before if you want to use it... */
if (ncp_get_fs_info(result->mount_fid, &(result->i))) {
free(result);
return errno;
}
#ifdef SIGNATURES
ncp_sign_init_perm(result);
#endif
if (!ncp_get_private_key(result, NULL, &klen)) {
if (klen > 10) {
result->connState |= CONNECTION_AUTHENTICATED;
}
}
result->bcast_state = NWCC_BCAST_PERMIT_UNKNOWN;
*conn = result;
return 0;
#else
return ENOPKG; /* better error code, anyone? */
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
int ncp_open_fd(int fd, struct ncp_conn** conn) {
int fd2;
int err;
fd2 = dup(fd);
if (fd2 == -1)
return errno;
err = ncp_do_open_fd(fd2, conn);
if (err)
close(fd2);
return err;
}
int
ncp_open_mount(const char *mount_point, struct ncp_conn** conn)
{
int fd;
int err;
fd = open(mount_point, O_RDONLY, 0);
if (fd == -1)
return errno;
err = ncp_do_open_fd(fd, conn);
if (err) {
close(fd);
return err;
}
/* returning NULL is not critical */
(*conn)->mount_point = strdup(mount_point);
return 0;
}
static long
ncp_user_disconnect(struct ncp_conn *conn)
{
static const unsigned char zero = 0;
long result;
switch (conn->nt) {
case NT_IPX:
case NT_UDP:
result = do_ncp_call(conn, NCP_DEALLOC_SLOT_REQUEST, 1, &zero, 1);
break;
case NT_TCP:
result = do_ncp_tcp_call(conn, NCP_DEALLOC_SLOT_REQUEST, 1, &zero, 1);
break;
default:
result = ECONNABORTED;
break;
}
if (result) {
return result;
}
close(conn->ncp_sock);
if (conn->wdog_sock != -1) {
close(conn->wdog_sock);
}
if (conn->wdog_pipe != -1) {
unsigned char dummy[1];
int res;
res = send(conn->wdog_pipe, "Q", 1, MSG_NOSIGNAL);
/* If write failed, we should not wait for child */
if (res == 1) {
read(conn->wdog_pipe, dummy, 1);
}
close(conn->wdog_pipe);
}
return 0;
}
static long
ncp_do_close(struct ncp_conn *conn)
{
long result;
switch (conn->is_connected) {
case CONN_PERMANENT:
if (conn->global_fd != -1)
close(conn->global_fd);
result = close(conn->mount_fid);
ncpt_mutex_lock(&nds_ring_lock);
list_del(&conn->nds_ring);
conn->nds_conn = NULL;
ncpt_mutex_unlock(&nds_ring_lock);
/* call NWDSReleaseDSConnection... */
conn->state++;
break;
case CONN_TEMPORARY:
if (conn->global_fd != -1)
close(conn->global_fd);
result = ncp_user_disconnect(conn);
ncpt_mutex_lock(&nds_ring_lock);
list_del(&conn->nds_ring);
conn->nds_conn = NULL;
ncpt_mutex_unlock(&nds_ring_lock);
/* call NWDSReleaseDSConnection... */
conn->state++;
break;
case CONN_KERNELBASED:
if (conn->global_fd != -1)
close(conn->global_fd);
result = close(conn->mount_fid);
ncpt_mutex_lock(&nds_ring_lock);
list_del(&conn->nds_ring);
conn->nds_conn = NULL;
ncpt_mutex_unlock(&nds_ring_lock);
/* call NWDSReleaseDSConnection... */
conn->state++;
break;
case NOT_CONNECTED:
result = 0;
break;
default:
result = -1;
break;
}
conn->is_connected = NOT_CONNECTED;
if (ncpt_atomic_read(&conn->store_count))
return 0;
ncpt_mutex_lock(&conn_lock);
list_del(&conn->conn_ring);
ncpt_mutex_unlock(&conn_lock);
if (conn->mount_point) {
free(conn->mount_point);
conn->mount_point = NULL;
}
if (conn->serverInfo.serverName) {
free(conn->serverInfo.serverName);
conn->serverInfo.serverName = NULL;
}
if (conn->user) {
free(conn->user);
conn->user = NULL; /* To be safe */
}
if (conn->ncp_reply_buffer) {
free(conn->ncp_reply_buffer);
conn->ncp_reply_buffer = NULL;
conn->ncp_reply_alloc = 0;
}
if (conn->private_key) {
free(conn->private_key);
conn->private_key = NULL;
conn->private_key_len = 0;
}
ncpt_mutex_destroy(&conn->serverInfo.mutex);
ncpt_mutex_destroy(&conn->buffer_mutex);
free(conn);
return result;
}
long
ncp_conn_release(struct ncp_conn *conn) {
if (!ncpt_atomic_dec_and_test(&conn->store_count))
return 0;
if (ncpt_atomic_read(&conn->use_count))
return 0;
return ncp_do_close(conn);
}
long
ncp_close(struct ncp_conn *conn)
{
if (conn == NULL) {
return 0;
}
if (ncpt_atomic_read(&conn->use_count)) {
if (!ncpt_atomic_dec_and_test(&conn->use_count))
return 0;
return ncp_do_close(conn);
} else
return NWE_REQUESTER_FAILURE; /* buggg ! */
}
int
ncp_get_mount_uid(int fid, uid_t* uid)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
unsigned long k2_uid;
__kernel_uid_t k_uid;
int err;
err = ioctl(fid, NCP_IOC_GETMOUNTUID2, &k2_uid);
if (!err) {
*uid = k2_uid;
return 0;
}
if (errno != -EINVAL)
return err;
err = ioctl(fid, NCP_IOC_GETMOUNTUID, &k_uid);
if (err)
return err;
*uid = k_uid;
return 0;
#else
errno = ENOPKG;
return -1;
#endif /* NCP_KERNEL_NCPFS_AVAILABLE */
}
struct ncp_conn_ent *
ncp_get_conn_ent(FILE * filep)
{
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
static struct ncp_conn_ent entry;
static char server[2 * NCPFS_MAX_CFG_USERNAME];
char *user;
struct mntent *mnt_ent;
int fid;
memset(server, 0, sizeof(server));
memset(&entry, 0, sizeof(entry));
while ((mnt_ent = getmntent(filep)) != NULL) {
if (strcmp(mnt_ent->mnt_type, "ncpfs") &&
strcmp(mnt_ent->mnt_type, "ncp")) {
continue;
}
if (strlen(mnt_ent->mnt_fsname) >= sizeof(server)) {
continue;
}
strcpy(server, mnt_ent->mnt_fsname);
user = strchr(server, '/');
if (user == NULL) {
continue;
}
*user = '\0';
user += 1;
entry.user = user;
if ((strlen(server) >= sizeof(entry.server))
|| (strlen(mnt_ent->mnt_dir) >= sizeof(entry.mount_point))) {
continue;
}
strcpy(entry.server, server);
strcpy(entry.mount_point, mnt_ent->mnt_dir);
fid = open(entry.mount_point, O_RDONLY, 0);
if (fid == -1) {
continue;
}
if (ncp_get_mount_uid(fid, &entry.uid) != 0) {
close(fid);
continue;
}
close(fid);
return &entry;
}
#else
errno = ENOPKG;
#endif
return NULL;
}
static struct ncp_conn_spec *
ncp_get_nwc_ent(FILE * nwc)
{
static struct ncp_conn_spec spec;
char line[512];
int line_len;
char *user;
char *password;
memset(&spec, 0, sizeof(spec));
spec.uid = getuid();
while (fgets(line, sizeof(line), nwc) != NULL) {
if ((line[0] == '\n')
|| (line[0] == '#')) {
continue;
}
line_len = strlen(line);
if (line[line_len - 1] == '\n') {
line[line_len - 1] = '\0';
}
user = strchr(line, '/');
password = strchr(user != NULL ? user : line, ' ');
if (password != NULL) {
*password = '\0';
password += 1;
}
if (user != NULL) {
*user = '\0';
user += 1;
if (strlen(user) >= sizeof(spec.user)) {
continue;
}
strcpy(spec.user, user);
}
if (strlen(line) >= sizeof(spec.server)) {
continue;
}
strcpy(spec.server, line);
if (password != NULL) {
while (*password == ' ') {
password += 1;
}
if (strlen(password) >= sizeof(spec.password)) {
continue;
}
strcpy(spec.password, password);
}
return &spec;
}
return NULL;
}
static NWCCODE
ncp_fopen_nwc(FILE** nwc)
{
char path[MAXPATHLEN];
char *home = NULL;
struct stat st;
FILE* f;
home = getenv("HOME");
if ((home == NULL)
|| (strlen(home) + sizeof(NWCLIENT) + 2 > sizeof(path))) {
return ENAMETOOLONG;
}
strcpy(path, home);
strcat(path, "/");
strcat(path, NWCLIENT);
if (stat(path, &st) != 0) {
return errno;
}
if (st.st_uid != getuid()) {
return EACCES;
}
if ((st.st_mode & (S_IRWXO | S_IRWXG)) != 0) {
return NCPLIB_INVALID_MODE;
}
f = fopen(path, "r");
if (!f) {
return errno;
}
*nwc = f;
return 0;
}
NWCCODE
ncp_find_conn_spec3(const char *server, const char *user, const char *password,
int login_necessary, uid_t uid, int allow_multiple_conns,
struct ncp_conn_spec *spec)
{
FILE *nwc;
struct ncp_conn_spec *nwc_ent;
if (!spec) {
return ERR_NULL_POINTER;
}
memset(spec, 0, sizeof(*spec));
spec->uid = uid;
if (server != NULL) {
if (strlen(server) >= sizeof(spec->server)) {
return ENAMETOOLONG;
}
strcpy(spec->server, server);
} else {
if (ncp_fopen_nwc(&nwc)) {
return NWE_SERVER_UNKNOWN;
}
nwc_ent = ncp_get_nwc_ent(nwc);
fclose(nwc);
if (nwc_ent == NULL) {
return NWE_SERVER_NO_CONN;
}
strcpy(spec->server, nwc_ent->server);
strcpy(spec->user, nwc_ent->user);
}
if (login_necessary == 0) {
memset(spec->user, 0, sizeof(spec->user));
memset(spec->password, 0, sizeof(spec->password));
return 0;
}
if (user != NULL) {
if (strlen(user) >= sizeof(spec->user)) {
return ENAMETOOLONG;
}
strcpy(spec->user, user);
}
str_upper(spec->user);
spec->login_type = NCP_BINDERY_USER;
if (!allow_multiple_conns) {
struct ncp_conn* conn;
if (ncp_open_permanent(spec, &conn) == 0) {
ncp_close(conn);
if (login_necessary &&
!(conn->connState & CONNECTION_AUTHENTICATED)) {
return NWE_USER_NO_NAME;
}
return 0;
}
}
if (password != NULL) {
if (strlen(password) >= sizeof(spec->password)) {
return ENAMETOOLONG;
}
strcpy(spec->password, password);
} else {
if (!ncp_fopen_nwc(&nwc)) {
while ((nwc_ent = ncp_get_nwc_ent(nwc)) != NULL) {
if ((strcasecmp(spec->server,
nwc_ent->server) != 0)
|| ((spec->user[0] != '\0')
&& (strcasecmp(spec->user,
nwc_ent->user) != 0))) {
continue;
}
strcpy(spec->user, nwc_ent->user);
strcpy(spec->password, nwc_ent->password);
break;
}
fclose(nwc);
}
}
if (spec->user[0] == 0) {
if (login_necessary == 1)
return NWE_USER_NO_NAME;
spec->password[0] = 0;
return 0;
}
if ((spec->password[0] == 0) && (password == NULL)) {
char *pwd;
if (!(isatty(0) && isatty(1))) {
return NCPLIB_PASSWORD_REQUIRED;
}
printf(_("Logging into %s as %s\n"),
spec->server, spec->user);
pwd = getpass(_("Password: "));
if (strlen(pwd) >= sizeof(spec->password)) {
return ENAMETOOLONG;
}
strcpy(spec->password, pwd);
} else {
if (strcmp(spec->password, NWC_NOPASSWORD) == 0) {
spec->password[0] = '\0';
}
}
str_upper(spec->server);
str_upper(spec->user);
str_upper(spec->password);
return 0;
}
struct ncp_conn_spec *
ncp_find_conn_spec2(const char *server, const char *user, const char *password,
int login_necessary, uid_t uid, int allow_multiple_conns, long *err)
{
static struct ncp_conn_spec spec;
NWCCODE nwerr;
nwerr = ncp_find_conn_spec3(server, user, password, login_necessary, uid,
allow_multiple_conns, &spec);
*err = nwerr;
if (!nwerr)
return &spec;
return NULL;
}
struct ncp_conn_spec *
ncp_find_conn_spec(const char *server, const char *user, const char *password,
int login_necessary, uid_t uid, long *err) {
return ncp_find_conn_spec2(server, user, password, login_necessary,
uid, 0, err);
}
struct ncp_conn *
ncp_initialize_2(int *argc, char **argv, int login_necessary,
int login_type, long *err, int required)
{
const char *server = NULL;
const char *user = NULL;
const char *password = NULL;
const char *address = NULL;
struct ncp_conn_spec spec;
struct ncp_conn *conn;
int i = 1;
NWCCODE nwerr;
static int get_argument(int arg_no, const char **target) {
int count = 1;
if (target != NULL) {
if (arg_no + 1 >= *argc) {
/* No argument to switch */
errno = EINVAL;
return -1;
}
*target = argv[arg_no + 1];
count = 2;
}
/* Delete the consumed switch from the argument list
and decrement the argument count */
while (count + arg_no < *argc) {
argv[arg_no] = argv[arg_no + count];
arg_no += 1;
}
*argc -= count;
return 0;
}
*err = EINVAL;
while (i < *argc) {
if ((argv[i][0] != '-')
|| (strlen(argv[i]) != 2)) {
i += 1;
continue;
}
switch (argv[i][1]) {
case 'S':
if (get_argument(i, &server) != 0) {
return NULL;
}
continue;
case 'U':
if (get_argument(i, &user) != 0) {
return NULL;
}
continue;
case 'P':
if (get_argument(i, &password) != 0) {
return NULL;
}
if (password) {
char* opw = (char*)password;
password = strdup(password);
memset(opw, 0, strlen(opw));
}
continue;
case 'n':
if (get_argument(i, NULL) != 0) {
return NULL;
}
password = NWC_NOPASSWORD;
continue;
#ifdef NDS_SUPPORT
case 'b':
if (get_argument(i, NULL) != 0) {
return NULL;
}
bindery_only = 1;
continue;
#endif
#ifdef CONFIG_NATIVE_IP
case 'A':
if (get_argument(i, &address) != 0) {
return NULL;
}
continue;
#endif /* CONFIG_NATIVE_IP */
}
i += 1;
}
if ((!required) && !(server || user || password || address))
return NULL;
nwerr = ncp_find_conn_spec3(server, user, password, login_necessary,
getuid(), 0, &spec);
if (nwerr) {
*err = nwerr;
if (login_necessary == 1) {
return NULL;
}
return ncp_open(NULL, err);
}
spec.login_type = login_type;
if (login_necessary == 0) {
spec.user[0] = '\0';
}
nwerr = ncp_open_2(&conn, &spec, address);
*err = nwerr;
if (nwerr)
return NULL;
return conn;
}
struct ncp_conn *
ncp_initialize_as(int *argc, char **argv, int login_necessary,
int login_type, long *err)
{
return ncp_initialize_2(argc, argv, login_necessary,
login_type, err, 1);
}
struct ncp_conn *
ncp_initialize(int *argc, char **argv,
int login_necessary, long *err)
{
return ncp_initialize_as(argc, argv, login_necessary,
NCP_BINDERY_USER, err);
}
long
ncp_request(struct ncp_conn *conn, int function)
{
long result = ENOTCONN;
#ifdef NCP_TRACE_ENABLE
{
char *ppacket = conn->packet + sizeof(struct ncp_request_header);
if (conn->has_subfunction) {
NCP_TRACE("ncp_request: fn=%d subfn=%d", function, (int) ppacket[2]);
} else {
NCP_TRACE("ncp_request: fn=%d subfn?%d", function, (int) *ppacket);
}
DUMP_HEX("ncp_request: packet=", ppacket, ncp_packet_size(conn));
}
#endif /*def NCP_TRACE_ENABLE */
switch (conn->is_connected) {
case CONN_PERMANENT:
result = ncp_mount_request(conn, function);
break;
case CONN_TEMPORARY:
result = ncp_temp_request(conn, function);
break;
case CONN_KERNELBASED:
result = ncp_kernel_request(conn, function);
break;
default:
break;
}
#ifdef NCP_TRACE_ENABLE
NCP_TRACE("ncp_request: reply %ld", result);
if (result == 0) {
DUMP_HEX("ncp_request->reply=",
conn->ncp_reply + sizeof(struct ncp_reply_header),
conn->ncp_reply_size);
}
#endif /*def NCP_TRACE_ENABLE */
return result;
}
/****************************************************************************/
/* */
/* Helper functions */
/* */
/****************************************************************************/
struct nw_time_buffer
{
u_int8_t year __attribute__((packed));
u_int8_t month __attribute__((packed));
u_int8_t day __attribute__((packed));
u_int8_t hour __attribute__((packed));
u_int8_t minute __attribute__((packed));
u_int8_t second __attribute__((packed));
u_int8_t wday __attribute__((packed));
};
static time_t
nw_to_ctime(struct nw_time_buffer *source)
{
struct tm u_time;
memset(&u_time, 0, sizeof(u_time));
u_time.tm_sec = source->second;
u_time.tm_min = source->minute;
u_time.tm_hour = source->hour;
u_time.tm_mday = source->day;
u_time.tm_mon = source->month - 1;
u_time.tm_year = source->year;
if (u_time.tm_year < 80) {
u_time.tm_year += 100;
}
return mktime(&u_time);
}
void ncp_add_pstring(struct ncp_conn *conn, const char *s) {
int len = strlen(s);
assert_conn_locked(conn);
if (len > 255) {
ncp_printf(_("ncpfs: string too long: %s\n"), s);
len = 255;
}
ncp_add_byte(conn, len);
ncp_add_mem(conn, s, len);
return;
}
void
ncp_init_request(struct ncp_conn *conn)
{
ncp_lock_conn(conn);
conn->current_point = conn->packet + sizeof(struct ncp_request_header);
conn->has_subfunction = 0;
}
void
ncp_init_request_s(struct ncp_conn *conn, int subfunction)
{
ncp_init_request(conn);
ncp_add_word_lh(conn, 0); /* preliminary size */
ncp_add_byte(conn, subfunction);
conn->has_subfunction = 1;
}
/* Here the ncp calls begin
*/
static long
ncp_negotiate_buffersize(struct ncp_conn *conn,
size_t size, size_t *target)
{
NWCCODE err;
nuint8 rq[2];
nuint8 buf[2];
NW_FRAGMENT rp;
WSET_HL(rq, 0, size);
rp.fragAddress = buf;
rp.fragSize = 2;
err = NWRequestSimple(conn, 33, rq, 2, &rp);
if (err)
return err;
if (rp.fragSize < 2)
return NWE_INVALID_NCP_PACKET_LENGTH;
if (target)
*target = min(WVAL_HL(buf, 0), size);
return 0;
}
#ifdef SIGNATURES
static NWCCODE ncp_negotiate_size_and_options(struct ncp_conn *conn,
size_t size, int options,
size_t *ret_size, int *ret_options)
{
long result;
ncp_init_request(conn);
if (options & 2) {
ncp_add_word_hl(conn, size + 8);
} else {
ncp_add_word_hl(conn, size);
}
ncp_add_byte(conn, options);
if ((result = ncp_request(conn, 0x61)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 5) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
if (ncp_reply_word_hl(conn, 0) == 0)
*ret_size = size;
else
*ret_size = min(ncp_reply_word_hl(conn, 0), size);
*ret_options = ncp_reply_byte(conn, 4);
ncp_unlock_conn(conn);
return 0;
}
#endif
long
ncp_get_file_server_description_strings(struct ncp_conn *conn,
char target[512])
{
NW_FRAGMENT rp;
if (!target)
return NWE_PARAM_INVALID;
rp.fragAddress = target;
rp.fragSize = 512;
return NWRequestSimple(conn, NCPC_SFN(23,201), NULL, 0, &rp);
}
long
ncp_get_file_server_time(struct ncp_conn *conn, time_t * target)
{
NWCCODE result;
struct nw_time_buffer buf;
NW_FRAGMENT rp;
rp.fragAddress = &buf;
rp.fragSize = sizeof(buf);
result = NWRequestSimple(conn, 20, NULL, 0, &rp);
if (result)
return result;
if (rp.fragSize < sizeof(buf))
return NWE_INVALID_NCP_PACKET_LENGTH;
if (target)
*target = nw_to_ctime(&buf);
return 0;
}
long
ncp_set_file_server_time(struct ncp_conn *conn, time_t * source)
{
int year;
struct tm *utime = localtime(source);
nuint8 srvtime[6];
year = utime->tm_year;
if (year > 99) {
year -= 100;
}
BSET(srvtime, 0, year);
BSET(srvtime, 1, utime->tm_mon + 1);
BSET(srvtime, 2, utime->tm_mday);
BSET(srvtime, 3, utime->tm_hour);
BSET(srvtime, 4, utime->tm_min);
BSET(srvtime, 5, utime->tm_sec);
return NWRequestSimple(conn, NCPC_SFN(23,202), srvtime, 6, NULL);
}
long
ncp_get_file_server_information(struct ncp_conn *conn,
struct ncp_file_server_info *target)
{
long result;
ncp_init_request_s(conn, 17);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
memcpy(target, ncp_reply_data(conn, 0), sizeof(*target));
ncp_unlock_conn(conn);
target->MaximumServiceConnections
= htons(target->MaximumServiceConnections);
target->ConnectionsInUse
= htons(target->ConnectionsInUse);
target->MaxConnectionsEverUsed
= htons(target->MaxConnectionsEverUsed);
target->NumberMountedVolumes
= htons(target->NumberMountedVolumes);
return 0;
}
NWCCODE
ncp_get_file_server_information_2(struct ncp_conn *conn,
struct ncp_file_server_info_2 *target, size_t starget)
{
NWCCODE result;
switch (starget) {
case sizeof(struct ncp_file_server_info_2):
break;
default:
return EINVAL;
}
ncp_init_request_s(conn, 17);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (target) {
memcpy(target->ServerName, ncp_reply_data(conn, 0), 48);
target->ServerName[48] = 0;
target->FileServiceVersion = ncp_reply_byte(conn, 48);
target->FileServiceSubVersion = ncp_reply_byte(conn, 49);
target->MaximumServiceConnections = ncp_reply_word_hl(conn, 50);
target->ConnectionsInUse = ncp_reply_word_hl(conn, 52);
target->NumberMountedVolumes = ncp_reply_word_hl(conn, 54);
target->Revision = ncp_reply_byte(conn, 56);
target->SFTLevel = ncp_reply_byte(conn, 57);
target->TTSLevel = ncp_reply_byte(conn, 58);
target->MaxConnectionsEverUsed = ncp_reply_word_hl(conn, 59);
target->AccountVersion = ncp_reply_byte(conn, 61);
target->VAPVersion = ncp_reply_byte(conn, 62);
target->QueueVersion = ncp_reply_byte(conn, 63);
target->PrintVersion = ncp_reply_byte(conn, 64);
target->VirtualConsoleVersion = ncp_reply_byte(conn, 65);
target->RestrictionLevel = ncp_reply_byte(conn, 66);
target->InternetBridge = ncp_reply_byte(conn, 67);
target->MixedModePathFlag = ncp_reply_byte(conn, 68);
target->LocalLoginInfoCcode = ncp_reply_byte(conn, 69);
target->ProductMajorVersion = ncp_reply_word_hl(conn, 70);
target->ProductMinorVersion = ncp_reply_word_hl(conn, 72);
target->ProductRevisionVersion = ncp_reply_word_hl(conn, 74);
target->OSLanguageID = ncp_reply_byte(conn, 76);
target->_64BitOffsetsSupportedFlag = ncp_reply_byte(conn, 77);
}
ncp_unlock_conn(conn);
return 0;
}
long
ncp_get_connlist(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
int *returned_no, u_int8_t conn_numbers[256])
{
long result;
unsigned int cnlen;
if (!object_name || !returned_no || !conn_numbers) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 21);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 1) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
cnlen = ncp_reply_byte(conn, 0);
if (conn->ncp_reply_size < 1 + cnlen) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
*returned_no = cnlen;
memcpy(conn_numbers, ncp_reply_data(conn, 1), cnlen);
ncp_unlock_conn(conn);
return 0;
}
long
ncp_get_stations_logged_info(struct ncp_conn *conn,
u_int32_t connection,
struct ncp_bindery_object *target,
time_t * login_time)
{
long result;
ncp_init_request_s(conn, 28);
ncp_add_dword_lh(conn, connection);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 60) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
if (target) {
memset(target, 0, sizeof(*target));
target->object_id = ncp_reply_dword_hl(conn, 0);
target->object_type = ncp_reply_word_hl(conn, 4);
memcpy(target->object_name, ncp_reply_data(conn, 6),
sizeof(target->object_name));
target->object_flags = 0;
target->object_security = 0;
target->object_has_prop = 0;
}
if (login_time)
*login_time = nw_to_ctime((struct nw_time_buffer *)
ncp_reply_data(conn, 54));
ncp_unlock_conn(conn);
return 0;
}
long
ncp_get_internet_address(struct ncp_conn *conn,
u_int32_t connection,
struct sockaddr *target,
u_int8_t * conn_type)
{
long result;
u_int8_t ct;
union ncp_sockaddr* t2 = (union ncp_sockaddr*)target;
if (!target) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 26);
ncp_add_dword_lh(conn, connection);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
memset(target, 0, sizeof(*target));
ct = ncp_reply_byte(conn, 12);
if (conn_type)
*conn_type = ct;
if (ct == 11) { /* 11... why 11? Nobody knows... */
#ifdef NCP_IN_SUPPORT
t2->inet.sin_family = AF_INET;
memcpy(&(t2->inet.sin_addr.s_addr), ncp_reply_data(conn, 0), 4);
/* I'm not sure about port... it is always 0 on Netwares I checked */
memcpy(&(t2->inet.sin_port), ncp_reply_data(conn, 4), 2);
#endif
} else { /* 2... IPX, 3... internal network (doc lies Appletalk DDP) */
#ifdef NCP_IPX_SUPPORT
t2->ipx.sipx_family = AF_IPX;
memcpy(&(t2->ipx.sipx_network), ncp_reply_data(conn, 0), 4);
memcpy(&(t2->ipx.sipx_node), ncp_reply_data(conn, 4), 6);
memcpy(&(t2->ipx.sipx_port), ncp_reply_data(conn, 10), 2);
#endif
}
ncp_unlock_conn(conn);
return 0;
}
long
ncp_send_broadcast(struct ncp_conn *conn,
u_int8_t no_conn, const u_int8_t * connections,
const char *message)
{
long result;
if (!message || (no_conn && !connections)) {
return ERR_NULL_POINTER;
}
if (strlen(message) > 58) {
return NWE_SERVER_FAILURE;
}
ncp_init_request_s(conn, 0);
ncp_add_byte(conn, no_conn);
ncp_add_mem(conn, connections, no_conn);
ncp_add_pstring(conn, message);
result = ncp_request(conn, 21);
ncp_unlock_conn(conn);
return result;
}
long
ncp_send_broadcast2(struct ncp_conn *conn,
unsigned int conns, const unsigned int* connlist,
const char* message)
{
int i;
long result;
if (!message || (conns && !connlist)) {
return ERR_NULL_POINTER;
}
i = strlen(message);
if (i > 255) {
return NWE_SERVER_FAILURE;
}
if (conns > 350) /* max pkt len ~ 1KB */
/* maybe do it by handshaked length ? */
return NWE_SERVER_FAILURE;
ncp_init_request_s(conn, 0x0A);
ncp_add_word_lh(conn, conns);
for (;conns; --conns)
ncp_add_dword_lh(conn, *connlist++);
ncp_add_byte(conn, i);
ncp_add_mem(conn, message, i);
result = ncp_request(conn, 0x15);
ncp_unlock_conn(conn);
return result;
}
/*
* target is a 8-byte buffer
*/
long
ncp_get_encryption_key(struct ncp_conn *conn,
char *target)
{
NW_FRAGMENT rp;
NWCCODE err;
if (!target)
return NWE_PARAM_INVALID;
rp.fragAddress = target;
rp.fragSize = 8;
err = NWRequestSimple(conn, NCPC_SFN(23,23), NULL, 0, &rp);
if (err)
return err;
if (rp.fragSize < 8)
return NWE_INVALID_NCP_PACKET_LENGTH;
return 0;
}
long
ncp_get_bindery_object_id(struct ncp_conn *conn,
NWObjectType object_type,
const char *object_name,
struct ncp_bindery_object *target)
{
long result;
if (!object_name || !target) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 53);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 54) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
target->object_id = ncp_reply_dword_hl(conn, 0);
target->object_type = ncp_reply_word_hl(conn, 4);
memcpy(target->object_name, ncp_reply_data(conn, 6), 48);
target->object_flags = 0;
target->object_security = 0;
target->object_has_prop = 0;
ncp_unlock_conn(conn);
return 0;
}
long
ncp_get_bindery_object_name(struct ncp_conn *conn,
u_int32_t object_id,
struct ncp_bindery_object *target)
{
long result;
if (!target) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 54);
ncp_add_dword_hl(conn, object_id);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
target->object_id = ncp_reply_dword_hl(conn, 0);
target->object_type = ncp_reply_word_hl(conn, 4);
memcpy(target->object_name, ncp_reply_data(conn, 6),
NCP_BINDERY_NAME_LEN);
target->object_flags = 0;
target->object_security = 0;
target->object_has_prop = 0;
ncp_unlock_conn(conn);
return 0;
}
NWCCODE NWScanObject(NWCONN_HANDLE conn, const char *searchName,
NWObjectType searchType, NWObjectID *objID,
char objName[NCP_BINDERY_NAME_LEN + 1], NWObjectType *objType,
nuint8 *hasPropertiesFlag, nuint8 *objFlags,
nuint8 *objSecurity) {
NWCCODE result;
if (!searchName || !objID) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 55);
ncp_add_dword_hl(conn, *objID);
ncp_add_word_hl(conn, searchType);
ncp_add_pstring(conn, searchName);
/* fn: 23 , subfn: 55 */
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
*objID = ncp_reply_dword_hl(conn, 0);
if (objType) {
*objType = ncp_reply_word_hl(conn, 4);
}
if (objName) {
memcpy(objName, ncp_reply_data(conn, 6),
NCP_BINDERY_NAME_LEN);
objName[NCP_BINDERY_NAME_LEN] = 0;
}
if (hasPropertiesFlag) {
*hasPropertiesFlag = ncp_reply_byte(conn, 56);
}
if (objFlags) {
*objFlags = ncp_reply_byte(conn, 54);
}
if (objSecurity) {
*objSecurity = ncp_reply_byte(conn, 55);
}
ncp_unlock_conn(conn);
return 0;
}
long
ncp_scan_bindery_object(struct ncp_conn *conn,
NWObjectID last_id, NWObjectType object_type, const char *search_string,
struct ncp_bindery_object *target)
{
NWCCODE result;
nuint8 hasProperties, objFlags, objSecurity;
if (!target) {
return ERR_NULL_POINTER;
}
result = NWScanObject(conn, search_string, object_type, &last_id,
target->object_name, &object_type, &hasProperties,
&objFlags, &objSecurity);
if (!result) {
target->object_id = last_id;
target->object_type = object_type;
target->object_flags = objFlags;
target->object_security = objSecurity;
target->object_has_prop = hasProperties;
}
return result;
}
long
ncp_change_object_security(struct ncp_conn *conn,
NWObjectType object_type,
const char *object_name,
u_int8_t new_object_security) {
long result;
if (!object_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 56);
ncp_add_byte(conn, new_object_security);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_create_bindery_object(struct ncp_conn *conn,
NWObjectType object_type,
const char *object_name,
u_int8_t object_security,
u_int8_t object_status)
{
long result;
if (!object_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 50);
ncp_add_byte(conn, object_status);
ncp_add_byte(conn, object_security);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_delete_bindery_object(struct ncp_conn *conn,
NWObjectType object_type,
const char *object_name)
{
long result;
if (!object_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 51);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
NWCCODE
NWReadPropertyValue(NWCONN_HANDLE conn, const char *objName,
NWObjectType objType, const char *propertyName,
unsigned int segmentNum, nuint8 *segmentData,
nuint8 *moreSegments, nuint8 *flags) {
NWCCODE result;
if (!objName || !propertyName) {
return ERR_NULL_POINTER;
}
if (segmentNum >= 256) {
return NWE_PARAM_INVALID;
}
ncp_init_request_s(conn, 61);
ncp_add_word_hl(conn, objType);
ncp_add_pstring(conn, objName);
ncp_add_byte(conn, segmentNum);
ncp_add_pstring(conn, propertyName);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 130) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
if (segmentData) {
memcpy(segmentData, ncp_reply_data(conn, 0), 128);
}
if (moreSegments) {
*moreSegments = ncp_reply_byte(conn, 128);
}
if (flags) {
*flags = ncp_reply_byte(conn, 129);
}
ncp_unlock_conn(conn);
return 0;
}
long
ncp_read_property_value(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
int segment, const char *prop_name,
struct nw_property *target)
{
nuint8 moreSegments;
nuint8 flags;
NWCCODE result;
if (!target) {
return ERR_NULL_POINTER;
}
result = NWReadPropertyValue(conn, object_name, object_type, prop_name,
segment, target->value, &moreSegments, &flags);
if (!result) {
target->more_flag = moreSegments;
target->property_flag = flags;
}
return result;
}
long
ncp_scan_property(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
u_int32_t last_id, const char *search_string,
struct ncp_property_info *property_info)
{
long result;
if (!object_name || !search_string || !property_info) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 60);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_dword_hl(conn, last_id);
ncp_add_pstring(conn, search_string);
if ((result = ncp_request(conn, 23)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 24) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
memcpy(property_info->property_name, ncp_reply_data(conn, 0), 16);
property_info->property_flags = ncp_reply_byte(conn, 16);
property_info->property_security = ncp_reply_byte(conn, 17);
property_info->search_instance = ncp_reply_dword_hl(conn, 18);
property_info->value_available_flag = ncp_reply_byte(conn, 22);
property_info->more_properties_flag = ncp_reply_byte(conn, 23);
ncp_unlock_conn(conn);
return 0;
}
long
ncp_add_object_to_set(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name,
NWObjectType member_type,
const char *member_name)
{
long result;
if (!object_name || !property_name || !member_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 65);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_pstring(conn, property_name);
ncp_add_word_hl(conn, member_type);
ncp_add_pstring(conn, member_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_change_property_security(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name,
u_int8_t property_security)
{
long result;
if (!object_name || !property_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 59);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_byte(conn, property_security);
ncp_add_pstring(conn, property_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_create_property(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name,
u_int8_t property_flags, u_int8_t property_security)
{
long result;
if (!object_name || !property_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 57);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_byte(conn, property_flags);
ncp_add_byte(conn, property_security);
ncp_add_pstring(conn, property_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_delete_object_from_set(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name,
NWObjectType member_type,
const char *member_name)
{
long result;
if (!object_name || !property_name || !member_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 66);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_pstring(conn, property_name);
ncp_add_word_hl(conn, member_type);
ncp_add_pstring(conn, member_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_delete_property(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name)
{
long result;
if (!object_name || !property_name) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 58);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_pstring(conn, property_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_write_property_value(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const char *property_name,
u_int8_t segment,
const struct nw_property *property_value)
{
long result;
if (!object_name || !property_name || !property_value) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 62);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_byte(conn, segment);
ncp_add_byte(conn, property_value->more_flag);
ncp_add_pstring(conn, property_name);
ncp_add_mem(conn, property_value->value, 128);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_get_big_ncp_max_packet_size(struct ncp_conn *conn,
u_int16_t proposed_max_size,
u_int8_t proposed_security_flag,
u_int16_t * accepted_max_size,
u_int16_t * echo_socket,
u_int8_t * accepted_security_flag)
{
long result;
ncp_init_request(conn);
ncp_add_word_hl(conn, proposed_max_size);
ncp_add_byte(conn, proposed_security_flag);
if ((result = ncp_request(conn, 97)) != 0) {
ncp_unlock_conn(conn);
return result;
}
if (conn->ncp_reply_size < 5) {
ncp_unlock_conn(conn);
return NWE_INVALID_NCP_PACKET_LENGTH;
}
if (accepted_max_size)
*accepted_max_size = ncp_reply_word_hl(conn, 0);
if (echo_socket)
*echo_socket = ncp_reply_word_hl(conn, 2);
if (accepted_security_flag)
*accepted_security_flag = ncp_reply_byte(conn, 4);
ncp_unlock_conn(conn);
return 0;
}
#if 0
static long
ncp_verify_object_password(struct ncp_conn *conn,
const char *objName, NWObjectType objType,
const char *objPassword)
{
long result;
ncp_init_request_s(conn, 63);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_pstring(conn, passwd);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
#endif
static long
ncp_keyed_verify_password(struct ncp_conn *conn,
const struct ncp_bindery_object *object,
const unsigned char *key,
const unsigned char *passwd)
{
dword tmpID = htonl(object->object_id);
unsigned char buf[128];
unsigned char encrypted[8];
long result;
if (!passwd) {
return ERR_NULL_POINTER;
}
shuffle((byte *) & tmpID, passwd, strlen(passwd), buf);
nw_encrypt(key, buf, encrypted);
ncp_init_request_s(conn, 74);
ncp_add_mem(conn, encrypted, 8);
ncp_add_word_hl(conn, object->object_type);
ncp_add_pstring(conn, object->object_name);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
NWCCODE NWVerifyObjectPassword(NWCONN_HANDLE conn,
const char* objName,
NWObjectType objType,
const char* objPassword) {
NWCCODE result;
u_int8_t ncp_key[8];
struct ncp_bindery_object user;
if ((result = ncp_get_encryption_key(conn, ncp_key)) != 0) {
#if 0
if (!ncp_conn_trusted(conn))
result = ncp_verify_object_password(conn, objName,
objType, objPassword);
#endif
return result;
}
result = ncp_get_bindery_object_id(conn, objType, objName, &user);
if (result)
return result;
return ncp_keyed_verify_password(conn, &user, ncp_key, objPassword);
}
static NWCCODE ncp_sign_stop(NWCONN_HANDLE conn);
NWCCODE NWLogoutFromFileServer(NWCONN_HANDLE conn)
{
NWCCODE result;
ncp_init_request(conn);
result = ncp_request(conn, 25);
if (result == 0) {
conn->user_id = 0;
conn->user_id_valid = 1;
conn->state++;
conn->connState &= ~(CONNECTION_AUTHENTICATED | CONNECTION_LICENSED);
result = ncp_sign_stop(conn);
}
ncp_unlock_conn(conn);
return result;
}
long
ncp_login_encrypted(struct ncp_conn *conn,
const struct ncp_bindery_object *object,
const unsigned char *key,
const unsigned char *passwd)
{
dword tmpID;
unsigned char buf[128];
unsigned char encrypted[8];
long result;
if (!passwd || !key || !object) {
return ERR_NULL_POINTER;
}
tmpID = htonl(object->object_id);
shuffle((byte *) & tmpID, passwd, strlen(passwd), buf);
nw_encrypt(key, buf, encrypted);
ncp_init_request_s(conn, 24);
ncp_add_mem(conn, encrypted, 8);
ncp_add_word_hl(conn, object->object_type);
ncp_add_pstring(conn, object->object_name);
result = ncp_request(conn, 23);
if ((result == 0) || (result == NWE_PASSWORD_EXPIRED)) {
long result2;
/* we must not set user_id here, we do not know it.
We know special `password ID' here only. */
conn->user_id_valid = 0;
conn->state++;
conn->connState |= CONNECTION_AUTHENTICATED | CONNECTION_LICENSED;
memcpy(buf + 16, key, 8);
sign_init(buf, buf);
result2 = ncp_sign_start(conn, buf);
if (result2)
result = result2;
}
ncp_unlock_conn(conn);
return result;
}
long
ncp_login_unencrypted(struct ncp_conn *conn,
NWObjectType object_type, const char *object_name,
const unsigned char *passwd)
{
long result;
if (!object_name || !passwd) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 20);
ncp_add_word_hl(conn, object_type);
ncp_add_pstring(conn, object_name);
ncp_add_pstring(conn, passwd);
result = ncp_request(conn, 23);
if ((result == 0) || (result == NWE_PASSWORD_EXPIRED)) {
/* we do not have user_id here... */
conn->user_id_valid = 0;
conn->state++;
conn->connState |= CONNECTION_AUTHENTICATED | CONNECTION_LICENSED;
}
ncp_unlock_conn(conn);
return result;
}
long
ncp_change_login_passwd(struct ncp_conn *conn,
const struct ncp_bindery_object *object,
const unsigned char *key,
const unsigned char *oldpasswd,
const unsigned char *newpasswd)
{
long id;
unsigned char cryptkey[8];
unsigned char newpwd[16]; /* new passwd as stored by server */
unsigned char oldpwd[16]; /* old passwd as stored by server */
unsigned char len;
long result;
if (!object || !key || !oldpasswd || !newpasswd) {
return ERR_NULL_POINTER;
}
id = htonl(object->object_id);
memcpy(cryptkey, key, 8);
shuffle((byte *) & id, oldpasswd, strlen(oldpasswd), oldpwd);
shuffle((byte *) & id, newpasswd, strlen(newpasswd), newpwd);
nw_encrypt(cryptkey, oldpwd, cryptkey);
newpassencrypt(oldpwd, newpwd, newpwd);
newpassencrypt(oldpwd + 8, newpwd + 8, newpwd + 8);
if ((len = strlen(newpasswd)) > 63) {
len = 63;
}
len = ((len ^ oldpwd[0] ^ oldpwd[1]) & 0x7f) | 0x40;
ncp_init_request_s(conn, 75);
ncp_add_mem(conn, cryptkey, 8);
ncp_add_word_hl(conn, object->object_type);
ncp_add_pstring(conn, object->object_name);
ncp_add_byte(conn, len);
ncp_add_mem(conn, newpwd, 16);
result = ncp_request(conn, 23);
ncp_unlock_conn(conn);
return result;
}
long
ncp_login_user(struct ncp_conn *conn,
const unsigned char *username,
const unsigned char *password)
{
return ncp_login_object(conn, username, NCP_BINDERY_USER, password);
}
static long
ncp_login_object(struct ncp_conn *conn,
const unsigned char *username,
int login_type,
const unsigned char *password)
{
long result;
unsigned char ncp_key[8];
struct ncp_bindery_object user;
if ((result = ncp_get_encryption_key(conn, ncp_key)) != 0) {
return ncp_login_unencrypted(conn, login_type, username,
password);
}
if ((result = ncp_get_bindery_object_id(conn, login_type,
username, &user)) != 0) {
return result;
}
if ((result = ncp_login_encrypted(conn, &user,
ncp_key, password)) != 0) {
struct nw_property p;
struct ncp_prop_login_control *l
= (struct ncp_prop_login_control *) &p;
if (result != NWE_PASSWORD_EXPIRED)
{
return result;
}
fprintf(stderr, _("Your password has expired\n"));
if ((result = ncp_read_property_value(conn, NCP_BINDERY_USER,
username, 1,
"LOGIN_CONTROL",
&p)) == 0) {
fprintf(stderr, _("You have %d login attempts left\n"),
l->GraceLogins);
}
}
return 0;
}
static NWCCODE
ncp_sign_stop(UNUSED(NWCONN_HANDLE conn))
{
#ifdef SIGNATURES
return 0;
#if 0
NWCCODE result = 0;
printf("Conn %p, %d\n", conn, conn->sign_active);
if (conn->sign_active) {
conn->sign_active = 0;
switch (conn->is_connected) {
case NOT_CONNECTED:
break;
case CONN_TEMPORARY:
break;
case CONN_PERMANENT:
/* Oops. We cannot turn off active signatures */
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, NULL))
result = EOPNOTSUPP;
break;
case CONN_KERNELBASED:
/* Oops. We cannot turn off active signatures */
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT, NULL))
result = EOPNOTSUPP;
break;
}
}
return result;
#endif
#else
return 0;
#endif
}
long
ncp_sign_start(struct ncp_conn *conn, const char *sign_root)
{
#ifdef SIGNATURES
static const char init_last[16]=
{0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef,
0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10};
if (ncp_get_sign_wanted(conn)) {
memcpy(conn->sign_data.sign_root, sign_root, 8);
memcpy(conn->sign_data.sign_last, init_last, 16);
conn->sign_active = 1;
switch (conn->is_connected) {
case NOT_CONNECTED:
break;
case CONN_TEMPORARY:
break;
case CONN_PERMANENT:
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT,
&conn->sign_data))
#endif
return NWE_SIGNATURE_LEVEL_CONFLICT;
break;
case CONN_KERNELBASED:
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
if (ioctl(conn->mount_fid, NCP_IOC_SIGN_INIT,
&conn->sign_data))
#endif
return NWE_SIGNATURE_LEVEL_CONFLICT;
break;
}
}
#endif
return 0;
}
#ifdef NDS_SUPPORT
long
ncp_send_nds_frag(struct ncp_conn *conn,
int ndsverb,
const char *inbuf, size_t inbuflen,
char *outbuf, size_t outbufsize, size_t *outbuflen)
{
long result;
size_t sizeleft, i;
size_t maxdatasize = 514;
int first = 1;
int firstReply = 1;
int fraghnd = -1;
int32_t ndsCode = -399;
size_t replyLen = 0;
size_t fragLen;
if (inbuflen && !inbuf) {
return ERR_NULL_POINTER;
}
if (outbuflen) *outbuflen = 0;
do {
sizeleft = maxdatasize;
ncp_init_request(conn);
ncp_add_byte(conn, 2);
ncp_add_dword_lh(conn, fraghnd);
if (first) {
ncp_add_dword_lh(conn, maxdatasize - 8);
ncp_add_dword_lh(conn, inbuflen + 12);
ncp_add_dword_lh(conn, 0);
ncp_add_dword_lh(conn, ndsverb);
ncp_add_dword_lh(conn, outbufsize);
sizeleft -= 25;
first = 0;
}
else
sizeleft -= 5;
i = (sizeleft > inbuflen) ? inbuflen : sizeleft;
if (i) ncp_add_mem(conn, inbuf, i);
inbuflen -= i;
inbuf += i;
if ((result = ncp_request(conn, 0x68)) != 0) {
ncp_unlock_conn(conn);
ncp_dprintf(_("Error in ncp_request\n"));
return result;
}
fragLen = ncp_reply_dword_lh(conn, 0);
if (fragLen < 4) {
ncp_unlock_conn(conn);
ncp_dprintf(_("Fragment too short\n"));
return NWE_INVALID_NCP_PACKET_LENGTH;
}
fraghnd = ncp_reply_dword_lh(conn, 4);
fragLen -= 4;
if (fragLen) {
int hdr;
if (firstReply) {
ndsCode = ncp_reply_dword_lh(conn, 8);
hdr = 12;
fragLen -= 4;
firstReply = 0;
} else {
hdr = 8;
}
if (fragLen > outbufsize) {
ncp_unlock_conn(conn);
ncp_dprintf(_("Fragment too large, len=%d, max=%d\n"), fragLen, outbufsize);
return NWE_BUFFER_OVERFLOW;
}
if (outbuf) {
memcpy(outbuf, ncp_reply_data(conn, hdr), fragLen);
outbuf += fragLen;
}
replyLen += fragLen;
} else {
/* if reply len == 0 then we must have something to transmit */
/* otherwise it can cause endless loop */
if ((fraghnd != -1) && (inbuflen == 0)) {
ncp_unlock_conn(conn);
ncp_dprintf(_("Why next fragment?\n"));
return NWE_SERVER_FAILURE;
}
}
ncp_unlock_conn(conn);
if (fraghnd != -1) {
ncp_dprintf(_("Fragmented\n"));
}
} while (fraghnd != -1);
if (inbuflen || firstReply) {
ncp_dprintf(_("InBufLen after request=%d, FirstReply=%d\n"), inbuflen, firstReply);
return NWE_SERVER_FAILURE;
}
if (outbuflen) *outbuflen = replyLen;
if (ndsCode != 0) {
ncp_dprintf(_("NDS error %d\n"), ndsCode);
if ((ndsCode < 0) && (ndsCode > -256))
return -ndsCode | NWE_SERVER_ERROR;
else
return ndsCode;
}
return 0;
}
long
ncp_send_nds(struct ncp_conn *conn, int fn,
const char *data_in, size_t data_in_len,
char *data_out, size_t data_out_max, size_t *data_out_len)
{
size_t i;
long err;
ncp_init_request(conn);
ncp_add_byte(conn, fn);
if (data_in) ncp_add_mem(conn, data_in, data_in_len);
if (!(err = ncp_request(conn, 0x68))) {
i = conn->ncp_reply_size;
if (i > data_out_max) i = data_out_max;
if (data_out)
memcpy(data_out, ncp_reply_data(conn, 0), i);
if (data_out_len) *data_out_len = i;
}
else
if (data_out_len) *data_out_len = 0;
ncp_unlock_conn(conn);
return err;
}
long
ncp_change_conn_state(struct ncp_conn *conn, int new_state)
{
nuint8 ns[4];
DSET_LH(ns, 0, new_state);
return NWRequestSimple(conn, NCPC_SFN(23,29), ns, 4, NULL);
}
#endif /* NDS_SUPPORT */
static int
ncp_attach_by_addr_tran(const struct sockaddr *target, nuint transport, struct ncp_conn** result) {
struct ncp_conn *conn;
int err;
if (!result) {
return ERR_NULL_POINTER;
}
*result = NULL;
conn = ncp_alloc_conn();
if (!conn) {
return ENOMEM;
}
err = ncp_connect_addr(conn, target, 1, transport);
if (err) {
ncp_close(conn);
return err;
}
*result = conn;
return 0;
}
NWCCODE NWCCOpenConnBySockAddr(const struct sockaddr* tran,
enum NET_ADDRESS_TYPE atran, nuint openState,
nuint reserved, NWCONN_HANDLE* conn) {
if (reserved != NWCC_RESERVED)
return NWE_PARAM_INVALID;
/* not yet supported */
if (openState & NWCC_OPEN_PUBLIC)
return NWE_PARAM_INVALID;
return ncp_attach_by_addr_tran(tran, atran, conn);
}
static int
ncp_attach_by_addr(const struct sockaddr *target, struct ncp_conn** result)
{
nuint transport;
if (!result || !target) {
return ERR_NULL_POINTER;
}
switch (target->sa_family) {
case AF_IPX:
transport = NT_IPX;
break;
case AF_INET:
if (getenv("NCP_OVER_TCP")) {
transport = NT_TCP;
} else {
transport = NT_UDP;
}
break;
case AF_UNIX:
transport = NT_TCP;
break;
default:
return EAFNOSUPPORT;
}
return ncp_attach_by_addr_tran(target, transport, result);
}
struct ncp_conn *
ncp_open_addr(const struct sockaddr *target, long *err) {
struct ncp_conn *conn;
if (!err) {
return NULL;
}
*err = ncp_attach_by_addr(target, &conn);
return conn;
}
long
ncp_get_broadcast_message(struct ncp_conn *conn, char message[256])
{
long result;
int length;
if (!message) {
return ERR_NULL_POINTER;
}
ncp_init_request_s(conn, 0x0B);
if ((result = ncp_request(conn, 0x15)) != 0) {
ncp_unlock_conn(conn);
ncp_init_request_s(conn, 0x01);
if ((result = ncp_request(conn, 0x15)) != 0) {
ncp_unlock_conn(conn);
return result;
}
}
length = ncp_reply_byte(conn, 0);
message[length] = 0;
memcpy(message, ncp_reply_data(conn, 1), length);
ncp_unlock_conn(conn);
return 0;
}
int
ncp_get_conn_type(struct ncp_conn* conn) {
if (conn) {
switch (conn->is_connected) {
case CONN_PERMANENT:
return NCP_CONN_PERMANENT;
case CONN_TEMPORARY:
return NCP_CONN_TEMPORARY;
case CONN_KERNELBASED:
return NCP_CONN_KERNELBASED;
default:;
}
}
return NCP_CONN_INVALID;
}
int
ncp_get_conn_number(struct ncp_conn* conn) {
int type;
type = ncp_get_conn_type(conn);
if (conn == NCP_CONN_INVALID)
return -ENOTCONN;
return conn->i.connection;
}
int ncp_get_fid(struct ncp_conn* conn) {
if (ncp_get_conn_type(conn) != NCP_CONN_PERMANENT)
return -1;
return conn->mount_fid;
}
NWCCODE
ncp_get_dentry_ttl(struct ncp_conn* conn, unsigned int* ttl) {
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
int fd = ncp_get_fid(conn);
u_int32_t kernel_ttl;
if (fd == -1)
return NWE_REQUESTER_FAILURE;
if (ioctl(fd, NCP_IOC_GETDENTRYTTL, &kernel_ttl)) {
if (errno != EINVAL)
return errno;
kernel_ttl = 0; /* old kernel... */
}
if (ttl)
*ttl = kernel_ttl;
return 0;
#else
return NWE_REQUESTER_FAILURE;
#endif
}
NWCCODE
ncp_set_dentry_ttl(struct ncp_conn* conn, unsigned int ttl) {
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
int fd = ncp_get_fid(conn);
u_int32_t kernel_ttl;
if (fd == -1)
return NWE_REQUESTER_FAILURE;
kernel_ttl = ttl;
if (ioctl(fd, NCP_IOC_SETDENTRYTTL, &kernel_ttl))
return errno;
return 0;
#else
return NWE_REQUESTER_FAILURE;
#endif
}
static NWCCODE
ncp_get_private_key_perm(struct ncp_conn* conn, void* pk, size_t* pk_len) {
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
int fd = ncp_get_fid(conn);
struct ncp_privatedata_ioctl npi;
if (fd == -1)
return NWE_REQUESTER_FAILURE;
if (!pk_len) {
return ERR_NULL_POINTER;
}
npi.data = pk;
if (pk)
npi.len = *pk_len;
else
npi.len = 0;
if (ioctl(fd, NCP_IOC_GETPRIVATEDATA, &npi))
return errno;
*pk_len = npi.len;
return 0;
#else
return NWE_REQUESTER_FAILURE;
#endif
}
static NWCCODE
ncp_get_private_key_temp(struct ncp_conn* conn, void* pk, size_t* pk_len) {
NWCCODE err = 0;
ncp_lock_conn(conn);
if (pk) {
size_t maxln = *pk_len;
if (maxln > conn->private_key_len) {
maxln = conn->private_key_len;
}
memcpy(pk, conn->private_key, maxln);
}
*pk_len = conn->private_key_len;
ncp_unlock_conn(conn);
return err;
}
NWCCODE
ncp_get_private_key(struct ncp_conn* conn, void* pk, size_t* pk_len) {
switch (ncp_get_conn_type(conn)) {
case NCP_CONN_TEMPORARY:
return ncp_get_private_key_temp(conn, pk, pk_len);
default:
return ncp_get_private_key_perm(conn, pk, pk_len);
}
}
static NWCCODE
ncp_set_private_key_perm(struct ncp_conn* conn, const void* pk, size_t pk_len) {
#ifdef NCP_KERNEL_NCPFS_AVAILABLE
int fd = ncp_get_fid(conn);
struct ncp_privatedata_ioctl npi;
if (fd == -1)
return NWE_REQUESTER_FAILURE;
if (pk_len && !pk)
return ERR_NULL_POINTER;
npi.data = (void*)pk;
npi.len = pk_len;
if (ioctl(fd, NCP_IOC_SETPRIVATEDATA, &npi))
return errno;
return 0;
#else
return NWE_REQUESTER_FAILURE;
#endif
}
static NWCCODE
ncp_set_private_key_temp(struct ncp_conn* conn, const void* pk, size_t pk_len) {
void *keydata;
void *oldkeydata;
keydata = malloc(pk_len);
if (!keydata)
return ENOMEM;
mlock(keydata, pk_len);
memcpy(keydata, pk, pk_len);
ncp_lock_conn(conn);
oldkeydata = conn->private_key;
conn->private_key = keydata;
conn->private_key_len = pk_len;
ncp_unlock_conn(conn);
free(oldkeydata);
return 0;
}
NWCCODE
ncp_set_private_key(struct ncp_conn* conn, const void* pk, size_t pk_len) {
switch (ncp_get_conn_type(conn)) {
case NCP_CONN_TEMPORARY:
return ncp_set_private_key_temp(conn, pk, pk_len);
default:
return ncp_set_private_key_perm(conn, pk, pk_len);
}
}
NWCCODE
ncp_next_conn(NWCONN_HANDLE conn, NWCONN_HANDLE* next_conn) {
struct list_head* h;
if (!next_conn) {
return ERR_NULL_POINTER;
}
ncpt_mutex_lock(&conn_lock);
for (h = conn ? conn->conn_ring.next : conn_list.next; h != &conn_list; h = h->next) {
NWCONN_HANDLE c2 = list_entry(h, struct ncp_conn, conn_ring);
if (c2->is_connected == NOT_CONNECTED)
continue;
if (ncpt_atomic_read(&c2->use_count)) {
/* FIXME. use_count has to be redone - either
with conditional store, or with mutex. Code
never expected that library can return
connection handle from internal list */
ncpt_atomic_inc(&c2->use_count);
ncpt_mutex_unlock(&conn_lock);
*next_conn = c2;
return 0;
}
}
ncpt_mutex_unlock(&conn_lock);
return NWE_REQUESTER_FAILURE;
}
#ifdef NCP_DEBUG
static FILE* ncp_do_FILE = NULL;
static const char* ncp_do_filename = NULL;
static size_t ncp_do_filename_len = 0;
static pid_t ncp_do_lastpid = 0;
static ncpt_mutex_t ncp_do_lock = NCPT_MUTEX_INITIALIZER;
static void ncp_do_printf(const char* fmt, ...) {
va_list ap;
ncpt_mutex_lock(&ncp_do_lock);
if (ncp_do_filename) {
pid_t cpid;
cpid = getpid();
if (cpid != ncp_do_lastpid) {
char* nfn;
if (ncp_do_FILE) {
fclose(ncp_do_FILE);
ncp_do_FILE = NULL;
}
nfn = alloca(ncp_do_filename_len + 20);
memcpy(nfn, ncp_do_filename, ncp_do_filename_len);
sprintf(nfn + ncp_do_filename_len, ".%u", cpid);
ncp_do_FILE = fopen(nfn, "wt");
if (ncp_do_FILE) {
ncp_do_lastpid = cpid;
}
}
}
if (ncp_do_FILE) {
va_start(ap, fmt);
vfprintf(ncp_do_FILE, fmt, ap);
va_end(ap);
}
ncpt_mutex_unlock(&ncp_do_lock);
}
static void ncp_do_init(void) {
ncp_do_FILE = stdout;
if (getuid() == geteuid()) {
const char* ncp_do;
ncp_do = getenv("NCP_DEBUG_OUTPUT");
if (ncp_do) {
ncp_do_filename_len = strlen(ncp_do);
ncp_do_filename = ncp_do;
}
}
}
static void ncp_do_fini(void) {
ncpt_mutex_lock(&ncp_do_lock);
if (ncp_do_FILE && ncp_do_filename) {
fclose(ncp_do_FILE);
ncp_do_FILE = NULL;
}
ncpt_mutex_unlock(&ncp_do_lock);
}
static void ncp_debug_help(void) __attribute((noreturn));
static void ncp_debug_help(void) {
fprintf(stderr, "Valid options for the NCP_DEBUG environment variable are:\n"
"\n"
" cleaup List NCP connections opened at exit\n"
" all All options above\n"
" help Display this help text\n"
"\n"
"To direct the debugging output into a file instead of standard output\n"
"a filename can be specified using the NCP_DEBUG_OUTPUT environment variable.\n");
exit(248);
}
static void ncp_debug_usage(int len, const char* ncpdebug) {
fprintf(stderr, "warning: NCP debug option `%*.*s\' unknown: try NCP_DEBUG=help\n", len, len, ncpdebug);
}
static int ncp_debug_show_cleanup = 0;
static void ncp_init(void) __attribute__((constructor));
static void ncp_init(void) {
const char* ncpdebug;
size_t ln;
ncpdebug = getenv("NCP_DEBUG");
if (!ncpdebug) {
return;
}
while (*ncpdebug) {
ln = strcspn(ncpdebug, ",:;");
if (ln) {
if (ln == 4 && !memcmp(ncpdebug, "help", 4)) {
ncp_debug_help();
} else if (ln == 7 && !memcmp(ncpdebug, "cleanup", 7)) {
ncp_debug_show_cleanup = 1;
} else if (ln == 3 && !memcmp(ncpdebug, "all", 3)) {
ncp_debug_show_cleanup = 1;
} else {
ncp_debug_usage(ln, ncpdebug);
}
}
ncpdebug += ln;
ln = strspn(ncpdebug, ",:;");
ncpdebug += ln;
}
ncp_do_init();
}
static void ncp_fini(void) __attribute__((destructor));
static void ncp_fini(void) {
struct list_head* h;
unsigned int cnt = 0;
if (ncp_debug_show_cleanup) {
ncpt_mutex_lock(&conn_lock);
for (h = conn_list.next; h != &conn_list; h = h->next) {
char fsname[NCP_BINDERY_NAME_LEN + 1];
NWCCODE err;
NWCONN_HANDLE c2 = list_entry(h, struct ncp_conn, conn_ring);
ncp_do_printf("Connection %p: store count=%u, use count=%u\n",
c2, ncpt_atomic_read(&c2->store_count), ncpt_atomic_read(&c2->use_count));
ncp_do_printf(" NDS connection: %p\n", c2->nds_conn);
ncp_do_printf(" User: %s\n", c2->user);
err = NWGetFileServerName(c2, fsname);
if (err)
sprintf(fsname, "<%s>", strnwerror(err));
ncp_do_printf(" Server name: %s\n", fsname);
cnt++;
}
ncpt_mutex_unlock(&conn_lock);
ncp_do_printf("Found %u connections alive on exit!\n", cnt);
}
ncp_do_fini();
}
#endif