4364 lines
99 KiB
C
4364 lines
99 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 "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 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;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
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);
|
|
INIT_LIST_HEAD(&conn->nds_ring);
|
|
// conn->nds_conn = NULL;
|
|
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 ((wdog_sock = socket(PF_IPX, SOCK_DGRAM, IPXPROTO_IPX)) == -1) {
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
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_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
|