Files
mars-nwe/src/nwbind.c
Mario Fetka dd2675a32c
All checks were successful
Source release / source-package (push) Successful in 59s
docs: audit file server monitor stubs
2026-06-02 20:24:28 +02:00

3554 lines
147 KiB
C

/* nwbind.c */
#ifndef MARS_NWE_BUILD_DATE
#define MARS_NWE_BUILD_DATE "unknown"
#endif
/* NCP Bindery SUB-SERVER */
/* authentification and some message and queue handling */
/* (C)opyright (C) 1993,2000 Martin Stover, Marburg, Germany
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* history since 21-Apr-00
*
* mst:25-Apr-00: added login control routines from Paolo Prandini
* mst:25-Apr-00: added simple example for getting nwconn data
* mst:14-Aug-00: added patch from Poalo Prandini
* mck:14-Jun-2003: added patch from Marco Cavallini (M@K)
*
*/
#include "net.h"
#include <string.h>
#include <signal.h>
#include "nwdbm.h"
#include "unxlog.h"
#include "nwbind.h"
#include "nwqueue.h"
#include "sema.h"
/* next should be '1', is for testing only */
#define USE_PERMANENT_OUT_SOCKET 1
static char my_nwname[50];
static ipxAddr_t my_addr;
static time_t akttime;
static int ipx_out_fd=-1;
static int ncp_fd = 0; /* stdin */
static struct t_unitdata ud;
static uint8 ipx_in_data[IPX_MAX_DATA];
static uint8 ipx_pack_typ = 17;
static int rcv_flags = 0;
static ipxAddr_t from_addr; /* actual calling address */
static NCPREQUEST *ncprequest = (NCPREQUEST*)&ipx_in_data;
static int server_goes_down=0;
static void nwconn_callback(int connection, char *data, int data_len, int completition) ;
static void write_to_nwserv(int what, int connection, int mode,
char *data, int size)
{
switch (what) {
case 0x4444 : /* tell the wdog there's no need to look 0 */
/* activate wdogs to free connection 1 */
/* the connection ist closed 99 */
if (write(FD_NWSERV, &what, sizeof(int)) < 0) {}
if (write(FD_NWSERV, &connection, sizeof(int)) < 0) {}
if (write(FD_NWSERV, &mode, sizeof(int)) < 0) {}
break;
case 0x6666 : /* send to client that server holds message */
if (write(FD_NWSERV, &what, sizeof(int)) < 0) {}
if (write(FD_NWSERV, &connection, sizeof(int)) < 0) {}
break;
case 0xffff : /* tell nwserv to down the server */
if (write(FD_NWSERV, &what, sizeof(int)) < 0) {}
if (write(FD_NWSERV, &what, sizeof(int)) < 0) {}
break;
default : break;
}
}
#define nwserv_reset_wdog(connection) \
write_to_nwserv(0x4444, (connection), 0, NULL, 0)
#define nwserv_handle_msg(connection) \
write_to_nwserv(0x6666, (connection), 0, NULL, 0)
#define nwserv_down_server() \
write_to_nwserv(0xffff, 0, 0, NULL, 0)
static int max_nw_vols=MAX_NW_VOLS;
static int max_connections=MAX_CONNECTIONS;
static CONNECTION *connections=NULL;
static CONNECTION *act_c=(CONNECTION*)NULL;
static int internal_act=0;
// Check if the new cleartext password is acceptable or not
static int nw_valid_newpasswd(uint32 obj_id, char *newpasswd)
{
LOGIN_CONTROL lc;
int result;
XDPRINTF((99, 0, "Change password for user %x with '%s'", obj_id, newpasswd));
result = nw_get_login_control(obj_id, &lc);
if (result < 0) return(0); /* No restrictions available */
XDPRINTF((2, 0, "len(pwd)=%d limit=%d", strlen(newpasswd), lc.minimumPasswordLength));
if (strlen(newpasswd)<lc.minimumPasswordLength)
return (-0xd8); // PASSWORD TOO SHORT
return 0;
}
static int nw_test_time_access(uint32 obj_id)
/*
* Code from Matt Paley, Mark Robson, Paolo Prandini
*/
{
time_t t,expiry;
struct tm exptm;
struct tm *tm;
int half_hours;
LOGIN_CONTROL lc;
int result;
result=nw_get_login_control(obj_id,&lc);
if (result < 0) return(0); /* No time limits available */
/* Check if account disabled - MR */
if (lc.accountExpired) /* Disabled */
{
XDPRINTF((1, 0, "No access for user %x - disabled.",obj_id));
return (-0xdc);
}
/* Account expires at 23:59:59 on the expiry day :) */
if (lc.accountExpiresYear) {
exptm.tm_year = lc.accountExpiresYear;
exptm.tm_mon = lc.accountExpiresMonth-1;
exptm.tm_mday = lc.accountExpiresDay;
exptm.tm_hour = 23;
exptm.tm_min = 59;
exptm.tm_sec = 59;
expiry = mktime(&exptm);
} else expiry = 0;
time(&t);
tm = localtime(&t);
if (expiry>0) /* if expiry is enabled */
{
XDPRINTF((1,0,"user has expiry of %d but time is %d",expiry,t));
if (t>expiry) /* has it expired ? */
{
XDPRINTF((1, 0, "No access for user %x - expired.",obj_id));
return (-0xdc);
}
}
half_hours = tm->tm_wday*48 + tm->tm_hour*2 + ((tm->tm_min>=30)? 1 : 0);
XDPRINTF((5,0,"half_hours=%d",half_hours));
{
char s[200];
int i;
for (i=0;i<42;i++) sprintf(s+i*3,"%02x ",lc.timeBitMap[i]);
XDPRINTF((5,0,"TIMEBITMAP=%s",s));
}
if ((lc.timeBitMap[half_hours/8] & (1<<(half_hours % 8))) == 0) {
XDPRINTF((1, 0, "No access for user %x at day %d %02d:%02d",
obj_id, tm->tm_wday, tm->tm_hour, tm->tm_min));
return(-0xda); /* unauthorized login time */
}
/* Password expires at 23:59:59 on the expiry day :) */
if (lc.passwordExpiresYear) {
exptm.tm_year = lc.passwordExpiresYear;
exptm.tm_mon = lc.passwordExpiresMonth-1;
exptm.tm_mday = lc.passwordExpiresDay;
exptm.tm_hour = 23;
exptm.tm_min = 59;
exptm.tm_sec = 59;
expiry = mktime(&exptm);
} else expiry = 0;
if (expiry>0) /* if expiry is enabled */
{
XDPRINTF((2, 0,"user has password expiry of %d and time is %d",expiry,t));
if (t>expiry) /* has it expired ? */ {
if (lc.passwordGraceLogins==0) {
XDPRINTF((1, 0, "No access for user %x - password expired,no grace.",obj_id));
return (-0xde);
}
if (lc.passwordGraceLogins!=255) {
// Decrement grace logins
lc.passwordGraceLogins--;
nw_set_login_control(obj_id, &lc);
XDPRINTF((1, 0, "Access for user %x - password expired, grace=%d.",obj_id,
lc.passwordGraceLogins));
return (-0xdf);
}
}
}
return 0;
}
static int nw_test_adr_access(uint32 obj_id, ipxAddr_t *client_adr)
{
uint8 more_segments;
uint8 property_flags;
char *propname="NODE_CONTROL";
uint8 buff[200];
uint8 wildnode[]={0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
int segment = 1 ;
int i,j;
LOGIN_CONTROL lc;
/*
** Start by checking the authorized login stations
*/
int result=nw_get_prop_val_by_obj_id(obj_id, segment,
propname, strlen(propname),
buff, &more_segments, &property_flags);
if (result >=0 ) {
uint8 *p=buff;
int k=0;
result=-0xdb; /* unauthorized login station */
while (k++ < 12) {
if ( (IPXCMPNET(client_adr->net, p))
&& ( (IPXCMPNODE(client_adr->node, p+4) )
|| (IPXCMPNODE(wildnode, p+4))) ) {
result=0;
break;
}
p+=10;
}
} else {
// if bindery object not found, no station restrictions
result=0;
}
if (result<0) {
XDPRINTF((1, 0, "No access for user %x at Station %s",
obj_id, visable_ipx_adr(client_adr)));
return result;
}
// Read LOGIN_CONTROL object
result=nw_get_login_control(obj_id,&lc);
if (result<0) return 0;
/*
** Now check the number of concurrent connections
*/
for (i=j=0;i<max_connections;i++)
if (connections[i].active) {
// If the object logged in is the same requesting access now,
// increment the connection counter
if (connections[i].object_id==obj_id)
j++;
}
XDPRINTF((3, 0, "Found connections for user %x = %d", obj_id, j));
// If the number of active connections is >0 then check permissions
if ( (j>0) && GET_BE16(lc.maxConcurrentConnections) ) {
if (j >= GET_BE16(lc.maxConcurrentConnections)) {
XDPRINTF((1, 0, "No access for user %x - max connections=%d",obj_id,
(int)GET_BE16(lc.maxConcurrentConnections)));
return (-0xd9);
}
}
return 0;
}
int nw_test_adr_time_access(uint32 obj_id, ipxAddr_t *client_adr)
{
int result;
struct tm *tm;
time_t t;
LOGIN_CONTROL lc;
time(&t);
tm = localtime(&t);
if (obj_id==1 && (entry8_flags & 8)) {
if (nw_get_login_control(obj_id,&lc)==0) {
lc.lastLoginDate[0]=tm->tm_year;
lc.lastLoginDate[1]=tm->tm_mon+1;
lc.lastLoginDate[2]=tm->tm_mday;
lc.lastLoginDate[3]=tm->tm_hour;
lc.lastLoginDate[4]=tm->tm_min;
lc.lastLoginDate[5]=tm->tm_sec;
nw_set_login_control(obj_id,&lc);
}
return(0); /* no limits for SU */
}
result=nw_test_adr_access(obj_id, client_adr);
if (!result) result=nw_test_time_access(obj_id);
if ((result==0)||(result==-0xdf)) {
if (nw_get_login_control(obj_id,&lc)==0) {
lc.lastLoginDate[0]=tm->tm_year;
lc.lastLoginDate[1]=tm->tm_mon+1;
lc.lastLoginDate[2]=tm->tm_mday;
lc.lastLoginDate[3]=tm->tm_hour;
lc.lastLoginDate[4]=tm->tm_min;
lc.lastLoginDate[5]=tm->tm_sec;
nw_set_login_control(obj_id,&lc);
}
}
XDPRINTF((result?1:5, 0, "Access for user %x = %x.",obj_id,-result));
return(result);
}
int b_acc(uint32 obj_id, int security, int forwrite)
{
/* security levels
* 0 = anyone have access.
* 1 = all logged have access
* 2 = object logged have access
* 3 = only supervisor has access
* 4 = only internal access.
*/
char *acc_what=NULL;
char *acc_typ=NULL;
int errcode =0;
XDPRINTF((5,0, "b_acc for id=0x%lx, security=0x%x, forwrite=0x%x",
obj_id, security, forwrite));
if (internal_act || !act_c) return(0); /* allways full access to internal routines */
if (forwrite & 0xf) security >>= 4; /* writesecurity */
security &= 0xf;
if (!security) return(0); /* rights for all */
else if (security == 1) {
if (act_c->object_id > 0) return(0); /* rights for all logged */
} else if (security == 2) {
if ( act_c->object_id == obj_id
|| (act_c->id_flags&1) ) return(0); /* rights for the user */
} else if (security == 3 && (act_c->id_flags&1)) return(0);
switch (forwrite&0xf) {
case 0 : acc_what = "read"; break;
case 1 : acc_what = "write"; break;
case 2 : acc_what = "creat"; break;
case 3 : acc_what = "delete"; break;
case 4 : acc_what = "rename"; break;
case 5 : acc_what = "change security"; break;
default : acc_what = "?" ; break;
}
switch ( (forwrite >> 4) & 0xf) {
case 0 : acc_typ = "obj" ; break;
case 1 : acc_typ = "prop"; break;
default : acc_typ = "?"; break;
}
switch (forwrite) {
case 0x00 : errcode = -0xf2; break;
case 0x01 : errcode = -0xf8; break; /* should be changed */
case 0x02 : errcode = -0xf5; break;
case 0x03 : errcode = -0xf4; break;
case 0x04 : errcode = -0xf3; break;
case 0x10 : errcode = -0xf9; break;
case 0x11 : errcode = -0xf8; break;
case 0x12 : errcode = -0xf7; break;
case 0x13 : errcode = -0xf6; break;
default : errcode = -0xff; break;
}
XDPRINTF((2, 0, "b_acc no rights for 0x%x to %s %s",
act_c->object_id, acc_what, acc_typ));
return(errcode);
}
static void sent_down_message(void)
{
int k = -1;
server_goes_down++;
while (++k < max_connections) {
CONNECTION *cn=&connections[k];
if (cn->active) {
#if 0
strmaxcpy(cn->message, "SERVER IS GOING DOWN", 58);
#else
strmaxcpy(cn->message, "MARS_NWE IS DIEING", 58);
#endif
nwserv_handle_msg(k+1);
}
} /* while */
}
static void delete_nwconn_request(CONNECTION *c)
{
if (c && c->request_nwconn) {
xfree(c->request_nwconn->data);
xfree(c->request_nwconn);
}
}
static void open_clear_connection(int conn, int activate, uint8 *addr)
{
if (conn > 0 && --conn < max_connections) {
CONNECTION *c = &connections[conn];
c->active = activate;
c->message[0] = '\0';
c->broadcast_disabled = 0;
c->t_login = 0;
if (activate && addr) {
memcpy(&(c->client_adr), addr, sizeof(ipxAddr_t));
c->send_to_sock = GET_BE16(addr+sizeof(ipxAddr_t));
c->pid_nwconn = GET_BE32(addr+sizeof(ipxAddr_t)+sizeof(uint16));
} else { /* down connection */
if (c->object_id)
write_utmp(0, conn+1, c->pid_nwconn, &(c->client_adr), NULL);
if (c->count_semas) {
clear_conn_semas(c);
}
}
c->object_id = 0;
c->id_flags = 0;
delete_nwconn_request(c);
c->last_used_sequence=0;
}
}
static void get_login_time(uint8 login_time[], CONNECTION *cx)
{
struct tm *s_tm = localtime(&(cx->t_login));
login_time[0] = s_tm->tm_year;
login_time[1] = s_tm->tm_mon+1;
login_time[2] = s_tm->tm_mday;
login_time[3] = s_tm->tm_hour;
login_time[4] = s_tm->tm_min;
login_time[5] = s_tm->tm_sec;
login_time[6] = s_tm->tm_wday;
}
static int has_console_privileges(CONNECTION *c)
/*
* NCP 23/200 only tells the client whether the current connection has
* console operator privileges. MARS-NWE does not currently maintain a
* separate bindery/NDS console-operator list, so map this to the same
* supervisor-equivalence flag that is already computed at login time.
*/
{
return(c && c->object_id && (c->id_flags & 1));
}
static int build_login_response(uint8 *responsedata, uint32 obj_id)
{
uint8 pw_name[40];
uint8 *p;
int result;
int i;
act_c->object_id = obj_id; /* actuell Object ID */
act_c->t_login = akttime; /* and login Time */
act_c->id_flags = 0;
internal_act=1;
if (HAVE_SU_RIGHTS(obj_id)) {
act_c->id_flags|=1; /* supervisor equivalence */
}
get_guid((int*) responsedata,
(int*) (responsedata+sizeof(int)),
obj_id, pw_name);
*((int*)(responsedata+2*sizeof(int))) = act_c->id_flags;
*((uint32*) (responsedata+3*sizeof(int))) = obj_id;
result=4*sizeof(int);
p = responsedata + result;
i = (uint8)get_groups_i_m_in(obj_id, (uint32*) (p+sizeof(int)) );
*(int*)(p) = i;
result+= (sizeof(int)+i*sizeof(uint32));
p=responsedata + result;
i=strlen(pw_name);
*p = (uint8) i;
memcpy(p+1, pw_name, i);
result += ( i + 1 );
write_utmp(1, act_connection, act_c->pid_nwconn,
&(act_c->client_adr), pw_name);
internal_act=0;
return(result);
}
/*
* ncp21_store_broadcast_message
*
* Store a pending broadcast message for one target connection and notify the
* corresponding nwconn process. The helper returns the old NCP 21/00 status
* byte by default and the newer NCP 21/10 status byte when new_status is set.
*/
static uint8 ncp21_store_broadcast_message(int connr, uint8 *msg,
int msglen, int new_status)
{
uint8 result = new_status ? 0x01 : 0xff; /* illegal/bad station */
CONNECTION *cn;
if (connr <= 0 || --connr >= max_connections)
return(result);
cn = &connections[connr];
if (!cn->active)
return(new_status ? 0x02 : 0xff); /* not logged/target bad */
if (cn->broadcast_disabled)
return(new_status ? 0x03 : 0xff); /* not accepting messages */
if (cn->message[0])
return(new_status ? 0x04 : 0xfc); /* already holds message */
strmaxcpy(cn->message, msg, min(58, msglen));
nwserv_handle_msg(connr+1);
return(0);
}
static void handle_fxx(int gelen, int func)
{
IPX_DATA ipxoutdata;
NCPRESPONSE *ncpresponse = (NCPRESPONSE*)&ipxoutdata;
uint8 *responsedata = ((uint8*)&ipxoutdata)+sizeof(NCPRESPONSE);
uint8 *requestdata = ((uint8*)ncprequest)+sizeof(NCPREQUEST);
#if 0
uint8 len = *(requestdata+1);
#endif
uint8 ufunc;
uint8 *rdata;
uint8 completition = 0;
uint8 connect_status= 0;
int data_len = 0;
if (func==0x18||func==19) {
ufunc = 0;
rdata = requestdata;
} else if (func==0x20) {
ufunc = *requestdata;
rdata = requestdata+1;
} else {
ufunc = *(requestdata+2);
rdata = requestdata+3;
}
// MDEBUG(D_BIND_REQ,
{
int j = gelen - sizeof(NCPREQUEST);
XDPRINTF((1, 0, "KNCP 0x%x REQUEST:ufunc:0x%x", func, ufunc));
if (j > 0){
uint8 *p=requestdata;
XDPRINTF((1, 2, "len %d, DATA:", j));
while (j--) {
int c = *p++;
if (c > 32 && c < 127) XDPRINTF((1, 3, ",\'%c\'", (char) c));
else XDPRINTF((1, 3, ",0x%x", c));
}
XDPRINTF((1, 1, NULL));
}
}
// )
if (0x15 == func) {
/*
* NCP 0x2222/21 is the Message NCP function group forwarded from
* nwconn.c. For this group the old/new SDK request header is:
*
* word SubFuncStrucLen (Hi-Lo)
* byte SubFunctionCode
* ... subfunction payload
*
* handle_fxx() has already consumed that group header here:
*
* ufunc = requestdata[2]
* rdata = requestdata + 3
*
* Therefore all offsets below are relative to the first byte after
* SubFunctionCode. The code keeps the existing wire behavior; comments
* record the SDK layout and any parser differences only.
*
* Missing-endpoint audit for this group:
*
* present here: SDK 21/00, 21/01, 21/02, 21/03, 21/09,
* 21/10, and 21/11.
* not a server NCP in the PDF message table: the CLIB
* NWGetBroadcastMode/NWSetBroadcastMode helpers are
* requester-side mode helpers, not additional 21/04..08
* server subfunctions in the default audit set.
* later/out of scope: SDK 21/12 Connection Message Control is documented
* as NetWare 5.x and is not part of the default
* NetWare-through-3.x compatibility target.
*/
switch (ufunc) { /* Messages */
case 0x0 : { /* Send Broadcast Message (old) */
/*
* NCP 0x2222/21/00 Send Broadcast Message (old).
* SDK payload after SubFunctionCode:
*
* byte ClientListLen
* byte TargetClientList[ClientListLen]
* byte MessageLen
* byte Message[MessageLen]
*
* SDK reply payload:
*
* byte ClientListLen
* byte SendStatus[ClientListLen]
*
* MARS-NWE parses the old one-byte connection list as documented and
* returns one status byte per target.
*/
int count_conns = (int)*(rdata); /* Number of connections */
uint8 *conns = rdata+1; /* connectionslist */
int msglen = *(conns+count_conns);
uint8 *msg = conns+count_conns+1;
uint8 *p = responsedata;
int one_found = 0;
int k = -1;
*p++ = (uint8) count_conns;
while (++k < count_conns) {
int connr = (int) (*conns++);
uint8 result = ncp21_store_broadcast_message(connr, msg, msglen, 0);
if (!result)
one_found++;
*p++ = result;
}
if (one_found) data_len = count_conns+1;
else completition=0xff;
}
break;
case 0x01: { /* Get Broadcast Message (old) */
/*
* NCP 0x2222/21/01 Get Broadcast Message (old).
* SDK payload after SubFunctionCode: empty.
* SDK reply payload:
*
* byte MessageLen
* byte Message[MessageLen]
*
* The stored per-connection broadcast buffer is returned and then
* cleared.
*/
*responsedata = (uint8) strmaxcpy(responsedata+1, act_c->message, 58);
act_c->message[0] = '\0';
data_len = (int)(*responsedata) + 1;
}
break;
case 0x02: { /* Disable Broadcasts */
/*
* NCP 0x2222/21/02 Disable Broadcasts.
* SDK payload after SubFunctionCode: empty.
* No reply payload; nwbind still sends the normal NCP response header
* with completion 0x00.
*/
act_c->broadcast_disabled = 1;
}
break;
case 0x03: { /* Enable Broadcasts */
/*
* NCP 0x2222/21/03 Enable Broadcasts.
* SDK payload after SubFunctionCode: empty.
* No reply payload; nwbind still sends the normal NCP response header
* with completion 0x00.
*/
act_c->broadcast_disabled = 0;
}
break;
case 0x09: { /* Broadcast to CONSOLE */
/*
* NCP 0x2222/21/09 Broadcast To Console.
* SDK payload after SubFunctionCode:
*
* byte MessageLen
* byte Message[MessageLen]
*
* No reply payload; nwbind still sends the normal NCP response header
* with completion 0x00. The current implementation logs the message
* text locally with the existing stderr console behavior.
*/
char message[60];
strmaxcpy(message, rdata+1, min(59, *rdata));
fprintf(stderr, "\n:%s\n", message);
}
break;
case 0x0a: { /* Send Broadcast Message (new) */
/*
* NCP 0x2222/21/10 Send Broadcast Message.
* SDK payload after SubFunctionCode:
*
* word ClientListCount
* long ClientList[ClientListCount]
* byte MessageLen
* byte Message[MessageLen]
*
* SDK reply payload:
*
* word RClientListCount
* long RClientCompFlag[ClientListCount]
*
* The NDK/Core Protocols PDF page documents long connection numbers
* and long completion flags here. The CLIB/WebSDK header prototype for
* NWSendBroadcastMessage instead exposes a 16-bit connection count, a
* uint16 connection list, and uint8 resultList entries, matching this
* compatibility parser. Keep this behavior until packet traces from a
* real client require the PDF long-list form.
*/
int count_conns = GET_BE16(rdata);
uint8 *conns = rdata+2;
int msglen = *(conns + count_conns*2);
uint8 *msg = conns + count_conns*2 + 1;
uint8 *p = responsedata;
int k;
U16_TO_BE16(count_conns, p);
p += 2;
for (k = 0; k < count_conns; k++) {
int connr = GET_BE16(conns + k*2);
*p++ = ncp21_store_broadcast_message(connr, msg, msglen, 1);
}
data_len = count_conns + 2;
}
break;
case 0x0b: { /* Get Broadcast Message (new) */
/*
* NCP 0x2222/21/11 Get Broadcast Message.
* SDK payload after SubFunctionCode: empty.
* SDK reply payload:
*
* byte MessageLen
* byte Message[MessageLen]
*
* Like the old get call, the current implementation returns and clears
* the per-connection broadcast buffer.
*/
*responsedata = (uint8) strmaxcpy(responsedata+1, act_c->message, 58);
act_c->message[0] = '\0';
data_len = (int)(*responsedata) + 1;
}
break;
default : completition=0xfb; /* not handled */
} /* switch */
} else if (0x16 == func) {
switch (ufunc) {
/* QUOTA support from: Matt Paley */
case 0x21 : /* Add User Disk Space Restriction, 0x2222/22/33 */
case 0x22 : /* Remove User Disk Space Restrictions, 0x2222/22/34 */
case 0x29 : { /* Get Object Disk Usage And Restrictions, 0x2222/22/41 */
/*
* Redirected NCP 0x2222/22 quota prehandler. nwconn.c dispatches
* these calls with func == 0x16 and ufunc as the wire subfunction;
* nwbind sees only the subfunction payload as rdata.
*
* Documented endpoints handled here:
* SDK 22/33 / wire 0x21 Add User Disk Space Restriction
* SDK 22/34 / wire 0x22 Remove User Disk Space Restrictions
* SDK 22/41 / wire 0x29 Get Object Disk Usage And Restrictions
*
* WebSDK / headers request payloads after SubFunctionCode:
* rdata[0] VolumeNumber
* rdata[1..4] ObjectID (Hi-Lo)
* rdata[5..8] DiskSpaceLimit (Lo-Hi, only for SDK 22/33)
*
* Parser comparison: ObjectID is read from the documented position
* for all three subfunctions. DiskSpaceLimit for SDK 22/33 is not
* consumed by this prehandler; handle_after_bind() receives only the
* uid/gid/permission tuple produced below.
*
* Internal reply to nwconn.c: 3 integers, uid, gid,
* 0=OK/1=Permission denied.
*/
uint32 id = GET_BE32(rdata+1);
internal_act=1;
if (get_guid((int*) responsedata, (int*)(responsedata+sizeof(int)),
id, (char *) NULL) != 0) {
completition = 0xff;
XDPRINTF((2, 0, "quota id-uid mapping failure %d 0x%x", ufunc, id));
}
internal_act=0;
/* OK if supervisor or trying to read (0x29) own limits */
if ( (act_c->id_flags&1) ||
(act_c->object_id == id && ufunc == 0x29))
((int *) responsedata)[2] = 0; /* OK */
else
((int *) responsedata)[2] = 1; /* Fail */
data_len = sizeof(int)*3;
}
break;
default : completition=0xfb; /* not handled */
}
} else if (0x17 == func) { /* Fileserver Enviro */
/*
* NCP 0x2222/23 File Server Environment forwarded from nwconn.c.
* handle_fxx() consumes the standard grouped header before this switch:
* requestdata[0..1] SubFuncStrucLen
* requestdata[2] SubFunctionCode
* rdata payload after SubFunctionCode
* Several queue subfunctions are preprocessed by nwconn before they reach
* this handler, so endpoint audits must include both the nwconn mutation
* and the final nwbind parser below.
*/
switch (ufunc) {
#if 0
case 0x00 : { /* SDK 23/00 / wire 0x00 Login User (old) */
/*
* NetWare 1.x/2.x legacy login shorthand for USER
* objects. SDK/PDF request payload after
* SubFunctionCode:
* byte UserNameLen
* byte UserName[UserNameLen]
* byte PasswordLen
* byte Password[PasswordLen]
* Not implemented here; keep as a documented future
* compatibility stub, not a NetWare-4-only endpoint.
*/
completition=0xfb;
}
break;
#endif
case 0x01 : { /* Change User Password OLD */
/*
* SDK 23/01 / wire 0x01 Change User Password (old).
* The current handler is a hard failure stub. The old
* PDF/WebSDK layout belongs to the NetWare 1.x/2.x
* compatibility bucket and should be filled in only when
* the legacy password-change semantics are implemented.
*/
completition=0xff;
}
break;
#if 0
case 0x02 : { /* SDK 23/02 / wire 0x02 Get User Connection List (old) */
/*
* SDK/PDF request payload after SubFunctionCode:
* byte UserNameLen
* byte UserName[UserNameLen]
* Reply: byte ListLen, then byte connection numbers.
* This earliest form is equivalent to the later object
* connection-list call for object type USER.
*/
completition=0xfb;
}
break;
#endif
#if 0
case 0x05 : { /* SDK 23/05 / wire 0x05 Get Station's Logged Info (old) */
/*
* NetWare 2.x compatibility call. Request payload after
* SubFunctionCode: byte TargetConnectionNumber.
* Reply is the fixed 258-byte legacy log record; the
* later SDK 23/22 and SDK 23/28 calls are implemented
* below as wire 0x16 and wire 0x1c.
*/
completition=0xfb;
}
break;
#endif
#if FUNC_17_02_IS_DEBUG
case 0x02 : { /* I hope this is call isn't used */
/* now missused as a debug switch :) */
struct XDATA {
uint8 nw_debug; /* old level */
} *xdata = (struct XDATA*) responsedata;
if (*rdata == NWBIND) {
xdata->nw_debug = (uint8) nw_debug;
nw_debug = (int) *(rdata+1);
data_len = 1;
} else completition=0xff;
}
break;
#endif
case 0x0c : { /* Verify Serialization */
/*
* SDK 23/12 / wire 0x0c Verify Serialization.
* SDK/PDF request payload is documented as a 4-byte
* ServerSerialNumber (Lo-Hi). The PDF table has a
* known-looking inconsistency that prints
* SubFunctionCode (212) even though the endpoint title
* is 23/12; keep this bucketed by the decimal endpoint
* number and do not treat it as wire 0xd4.
* Current code is still a hard failure stub.
*/
completition=0xff;
}
break;
case 0x0e : { /* Get Disk Utilization */
completition=0xff;
}
break;
#if 0
case 0x10 : set file information. handled in nwconn.
#endif
case 0x11 : { /* Get FileServer Info */
/*
* SDK 23/17 / wire 0x11 Get File Server Information.
* SDK/PDF request payload after SubFunctionCode is
* empty. The reply is the legacy server-info block: 48
* bytes server name, version/subversion, connection and
* volume counters, and feature-version bytes. Later
* PDF revisions name additional bytes after offset 76;
* this code returns the same fixed-size block but keeps
* those later fields inside the reserved tail.
*/
struct XDATA {
uint8 servername[48];
uint8 version; /* 2 or 3 */
uint8 subversion; /* 15 or 11 */
uint8 maxconnections[2];
uint8 connection_in_use[2];
uint8 max_volumes[2];
uint8 os_revision; /* 0 */
uint8 sft_level; /* 2 */
uint8 tts_level; /* 1 */
uint8 peak_connection[2];
uint8 accounting_version; /* 1 */
uint8 vap_version; /* 1 */
uint8 queuing_version; /* 1 */
uint8 print_server_version; /* 0 */
uint8 virtual_console_version; /* 1 */
uint8 security_level; /* 1 */
uint8 internet_bridge_version; /* 1 */
uint8 reserved[60];
} *xdata = (struct XDATA*) responsedata;
int k, i, h;
memset(xdata, 0, sizeof(struct XDATA));
strncpy(xdata->servername, my_nwname, sizeof(xdata->servername)-1);
if (!tells_server_version) {
xdata->version = 2;
xdata->subversion = 15;
} else {
xdata->version = 3;
xdata->subversion = (tells_server_version == 2)
? 12
: 11;
}
i=0;
h=0;
for (k=0; k < max_connections; k++) {
if (connections[k].active) {
i++;
h = k+1;
}
}
U16_TO_BE16(i, xdata->connection_in_use);
U16_TO_BE16(max_connections, xdata->maxconnections);
U16_TO_BE16(h, xdata->peak_connection);
U16_TO_BE16(max_nw_vols, xdata->max_volumes);
xdata->security_level=1;
/*
* if this level is 0
* you cannot install access restrictions.
*/
#ifdef _MAR_TESTS_1
xdata->sft_level=2;
xdata->tts_level=1;
xdata->accounting_version=1;
xdata->vap_version=1;
xdata->queuing_version=1;
xdata->virtual_console_version=1;
xdata->security_level=1;
xdata->internet_bridge_version=1;
#endif
data_len = sizeof(struct XDATA);
}
break;
case 0x12 : { /* Get Network Serial Number */
/*
* SDK 23/18 / wire 0x12 Get Network Serial Number.
* SDK/PDF request payload is empty. The PDF describes
* ServerSerialNumber and ApplicationNumber as Lo-Hi,
* while the current reply writes both with BE helpers;
* keep that as an audit item until an old requester is
* tested.
*/
struct XDATA {
uint8 serial_number[4];
uint8 appl_number[2];
} *xdata = (struct XDATA*) responsedata;
/* serial-number 4-Byte */
U32_TO_BE32(network_serial_nmbr, xdata->serial_number);
/* application-number 2-Byte */
U16_TO_BE16(network_appl_nmbr, xdata->appl_number);
data_len = sizeof(struct XDATA);
}
break;
case 0x13 : /* Get Connection Internet Address, old */
case 0x1a : { /* Get Connection Internet Address, new */
/*
* SDK 23/19 / wire 0x13 Get Internet Address (old):
* byte TargetConnection
* SDK 23/26 / wire 0x1a Get Internet Address:
* long TargetConnection (Lo-Hi)
* The old path reads the documented byte. The new path
* uses GET_32(), matching the documented Lo-Hi order, and
* appends ConnectionType=2 for NCP.
*/
int conn = (ufunc == 0x13)
? ((max_connections < 256)
? (int) *rdata
: act_connection)
: GET_32(rdata);
if (conn >0 && conn <= max_connections
&& connections[conn-1].active ) {
CONNECTION *cx=&(connections[conn-1]);
data_len = sizeof(ipxAddr_t);
memcpy(responsedata, (char*)&(cx->client_adr), data_len);
if (ufunc==0x1a) {
*(responsedata+data_len)=0x02; /* NCP connection */
data_len++;
}
} else {
XDPRINTF((1, 0, "Get Connection Internet Adress, Conn:%d of %d failed",
conn, max_connections));
completition = 0xff;
}
} break;
case 0x14 : { /* Login Objekt, unencrypted passwords */
/*
* SDK 23/20 / wire 0x14 Login Object.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ClientNameLen
* byte ClientName[ClientNameLen]
* byte PasswordLen
* byte Password[PasswordLen]
* Parser follows the documented layout and clears the
* plaintext password bytes after copying them.
*/
uint8 *p = rdata;
uint8 *p1 = p+3 + *(p+2); /* here is password */
int result;
NETOBJ obj;
char password[80];
obj.type = GET_BE16(p);
xstrmaxcpy(obj.name, p+3, (int) *(p+2));
upstr(obj.name);
xstrmaxcpy(password, p1+1, (int) *p1);
if (*p1) memset(p1+1, 0, *p1);
XDPRINTF((10, 0, "LOGIN unencrypted PW NAME='%s', PASSW='%s'",
obj.name, password));
if (0 == (result = find_obj_id(&obj))) {
if (password_scheme & PW_SCHEME_LOGIN) {
#if 0
if (obj.id == 1) {
result=-0xff; /* SUPERVISOR ever encryted !! */
XDPRINTF((1, 0, "Supervisor tried unencrypted LOGIN"));
} else
#endif
{
internal_act = 1;
result=nw_test_unenpasswd(obj.id, password);
internal_act = 0;
}
} else {
XDPRINTF((1, 0, "unencryted logins are not enabled"));
result=-0xff;
}
}
if ( (!result) && obj.type == 1 ) {
/* ..............^^^^^^^^^^^^^^^
* This check is necessary to avoid restriction on objects
* other than users! Paolo Prandini,mst:14-Aug-00
*/
internal_act = 1;
result = nw_test_adr_time_access(obj.id, &(act_c->client_adr));
internal_act = 0;
}
memset(password, 0, 50);
if (!result)
data_len = build_login_response(responsedata, obj.id);
else {
completition = (uint8) -result;
if (result==-0xdf) { /* PaPr,mst:25-Apr-00 */
data_len = build_login_response(responsedata, obj.id);
}
}
} break;
case 0x15 : { /* Get Object Connection List (old) */
/*
* SDK 23/21 / wire 0x15 Get Object Connection List (old).
* Request payload: ObjectType (Hi-Lo), ObjectNameLen,
* ObjectName. The SDK/PDF old reply is byte ListLen plus
* connection-number entries; this implementation returns
* one byte per connection, matching the low-byte legacy
* form used by old clients.
*/
uint8 *p = rdata;
int result;
NETOBJ obj;
obj.type = GET_BE16(p);
p+=2;
strmaxcpy((char*)obj.name, (char*)(p+1), (int) *(p));
upstr(obj.name);
result = find_obj_id(&obj);
if (!result){
int k=-1;
int count = 0;
p = responsedata+1;
while (++k < max_connections && count < 255) {
CONNECTION *cn= &connections[k];
if (cn->active && cn->object_id == obj.id) {
*p++=(uint8)k+1;
count++;
}
} /* while */
*responsedata = count;
data_len = 1 + count;
} else completition=(uint8)-result;
}
break;
case 0x16 : /* Get Connection Info, old */
case 0x1c : { /* Get Connection Info, new ( > 255 connections) */
/*
* SDK 23/22 / wire 0x16 Get Station's Logged Info (old):
* byte TargetConnectionNumber
* SDK 23/28 / wire 0x1c Get Station's Logged Info:
* long TargetConnectionNum (Lo-Hi)
* The reply shape matches the documented user-id, type,
* name, login-time, and reserved-byte block. The new path
* reads the 32-bit connection with GET_32().
*/
struct XDATA {
uint8 object_id[4];
uint8 object_type[2];
uint8 object_name[48];
uint8 login_time[7];
uint8 reserved;
} *xdata = (struct XDATA*) responsedata;
int conn = (ufunc == 0x16)
? ((max_connections < 256)
? (int) *rdata
: act_connection)
: GET_32(rdata);
memset(xdata, 0, sizeof(struct XDATA));
data_len = sizeof(struct XDATA);
if (conn && conn <= max_connections
&& connections[conn-1].active ) {
CONNECTION *cx=&(connections[conn-1]);
NETOBJ obj;
int result;
obj.id = cx->object_id;
result = nw_get_obj(&obj);
if (!result) {
memset(xdata, 0, sizeof(struct XDATA));
U32_TO_BE32(obj.id, xdata->object_id);
U16_TO_BE16(obj.type, xdata->object_type);
strncpy(xdata->object_name, obj.name, 48);
get_login_time(xdata->login_time, cx);
} /* else completition = (uint8)(-result); */
} else if (!conn || conn > max_connections) {
data_len = 0;
completition = 0xfd;
}
} break;
case 0x17 : { /* get crypt key */
/*
* SDK 23/23 / wire 0x17 Get Login Key.
* SDK/PDF request payload contains TargetConnection as a
* byte, but the current implementation ignores it and
* returns a freshly generated key for the active
* connection. Track that semantic difference in TODO.
*/
int k = sizeof(act_c->crypt_key);
uint8 *p = act_c->crypt_key;
uint8 *pp = responsedata;
data_len = k;
while (k--) *pp++ = *p++ = (uint8) rand();
/* if all here are same (1 or 2) then the resulting key is */
/* 00000000 */
if (password_scheme & PW_SCHEME_GET_KEY_FAIL)
completition=0xfb;
}
break;
case 0x18 : { /* crypt_keyed LOGIN */
/*
* SDK 23/24 / wire 0x18 Keyed Object Login.
* Request payload after SubFunctionCode:
* byte Key[8]
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Parser consumes the key first and then reads the
* documented object type/name fields.
*/
uint8 *p = rdata+sizeof(act_c->crypt_key);
NETOBJ obj;
int result;
obj.type = GET_BE16(p);
obj.id = 0;
xstrmaxcpy(obj.name, (char*)(p+3), *(p+2));
upstr(obj.name);
XDPRINTF((2, 0, "LOGIN CRYPTED PW NAME='%s'",obj.name));
if (0 == (result = find_obj_id(&obj))) {
internal_act = 1;
result=nw_test_passwd(obj.id, act_c->crypt_key, rdata);
internal_act = 0;
}
if ( (result>-1) && obj.type == 1 ) {
/* ..............^^^^^^^^^^^^^^^
* This check is necessary to avoid restriction on objects
* other than users! Paolo Prandini,mst:14-Aug-00
*/
internal_act = 1;
result = nw_test_adr_time_access(obj.id, &(act_c->client_adr));
internal_act = 0;
}
if (result > -1)
data_len = build_login_response(responsedata, obj.id);
else {
completition = (uint8) -result;
if (result==-0xdf) { /*PaPr,mst:25-Apr-00 */
data_len = build_login_response(responsedata, obj.id);
}
}
/*
* completition = 0xde means login time has expired
* completition = 0xdf means good login, but
* login time has expired, using grace logins
*/
}
break;
case 0x1B : { /* Get Object Connection List */
/*
* SDK 23/27 / wire 0x1b Get Object Connection List.
* Request payload: SearchConnNumber (Lo-Hi), ObjectType
* (Hi-Lo), ObjectNameLen, ObjectName. Reply is byte
* ConnListLen plus connection numbers. The code reads
* SearchConnNumber with GET_BE32() but writes returned
* connection numbers with U16_TO_16(); verify both widths
* and byte order against an old requester before changing.
*/
uint8 *p = rdata;
int result;
NETOBJ obj;
int searchnr = (int) GET_BE32(p);
p+=4;
obj.type = GET_BE16(p);
p+=2;
strmaxcpy((char*)obj.name, (char*)(p+1), (int) *(p));
upstr(obj.name);
result = find_obj_id(&obj);
if (!result){
int k = max(-1, searchnr-1);
int count = 0;
p = responsedata+1;
while (++k < max_connections && count < 255) {
CONNECTION *cn= &connections[k];
if (cn->active && cn->object_id == obj.id) {
U16_TO_16(k+1, p); /* LO-HI !! */
p+=2;
count++;
}
} /* while */
*responsedata = count;
data_len = 1 + count*2;
} else completition=(uint8)-result;
}
break;
case 0x32 : { /* Create Bindery Object */
/*
* SDK 23/50 / wire 0x32 Create Bindery Object.
* Request payload after SubFunctionCode:
* byte StatusFlags
* byte SecurityLevel
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Parser comparison: field order and ObjectType byte
* order match the documented layout. The include-level
* cross-check is NWCreateObject() in nwbindry.h.
*/
NETOBJ obj;
int result;
uint8 *p = rdata;
obj.flags = *p++;
obj.security = *p++;
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)p+3, (int) *(p+2));
result = nw_create_obj(&obj, 0);
if (result < 0) completition = (uint8) -result;
} break;
case 0x33 : { /* delete OBJECT */
/*
* SDK 23/51 / wire 0x33 Delete Bindery Object.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Parser comparison: matches the documented layout.
* Include cross-check: NWDeleteObject() in nwbindry.h.
*/
uint8 *p = rdata;
int result;
NETOBJ obj;
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)(p+3), (int) *(p+2));
result = nw_delete_obj(&obj);
if (result < 0) completition = (uint8) -result;
} break;
case 0x34 : { /* rename OBJECT, only SU */
/*
* SDK 23/52 / wire 0x34 Rename Object.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte OldObjectNameLen
* byte OldObjectName[OldObjectNameLen]
* byte NewObjNameLen
* byte NewObjectName[NewObjNameLen]
* Parser comparison: matches the documented order.
* The implementation additionally enforces local SU
* rights before calling nw_rename_obj().
* Include cross-check: NWRenameObject() in nwbindry.h.
*/
int result=-0xff;
if (act_c->id_flags&1) {
uint8 *p = rdata;
NETOBJ obj;
uint8 newname[256];
uint8 *p1 = p+3 + *(p+2); /* new Name Length */
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)(p+3), (int) *(p+2));
strmaxcpy((char*)newname, (char*)(p1+1), (int) *(p1));
result = nw_rename_obj(&obj, newname);
}
if (result) completition = (uint8) -result;
} break;
case 0x35 : { /* get Bindery Object ID */
/*
* SDK 23/53 / wire 0x35 Get Bindery Object ID.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Reply: ObjectID (Hi-Lo), ObjectType (Hi-Lo), and
* ObjectName[48]. Parser/reply comparison matches the
* documented layout.
* Include cross-check: NWGetObjectID() in nwbindry.h.
*/
struct XDATA {
uint8 object_id[4];
uint8 object_type[2];
uint8 object_name[48];
} *xdata = (struct XDATA*) responsedata;
uint8 *p = rdata;
int result;
NETOBJ obj;
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)(p+3), (int) *(p+2));
upstr(obj.name);
result = find_obj_id(&obj);
if (!result){
U32_TO_BE32(obj.id, xdata->object_id);
U16_TO_BE16(obj.type, xdata->object_type);
strncpy(xdata->object_name, obj.name, 48);
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
} break;
case 0x36 : { /* get Bindery Object Name */
/*
* SDK 23/54 / wire 0x36 Get Bindery Object Name.
* Request payload after SubFunctionCode:
* long ObjectID (Hi-Lo)
* Reply: ObjectID (Hi-Lo), ObjectType (Hi-Lo), and
* ObjectName[48]. Parser/reply comparison matches the
* documented layout.
* Include cross-check: NWGetObjectName() in nwbindry.h.
*/
struct XDATA {
uint8 object_id[4];
uint8 object_type[2];
uint8 object_name[48];
} *xdata = (struct XDATA*) responsedata;
uint8 *p = rdata;
int result;
NETOBJ obj;
obj.id = GET_BE32(p);
result = nw_get_obj(&obj);
if (!result){
U32_TO_BE32(obj.id, xdata->object_id);
U16_TO_BE16(obj.type, xdata->object_type);
strncpy(xdata->object_name, obj.name, 48);
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
} break;
case 0x37 : { /* Scan Bindery Object */
/*
* SDK 23/55 / wire 0x37 Scan Bindery Object.
* Request payload after SubFunctionCode:
* long LastObjectSeen (Hi-Lo)
* word SearchObjectType (Hi-Lo)
* byte SearchNameLen
* byte SearchObjectName[SearchNameLen]
* Reply includes ObjectID, ObjectType, ObjectName[48],
* ObjectFlags, ObjectSecurity, and ObjectHasProperties.
* Parser/reply comparison matches the documented layout.
* Include cross-check: NWScanObject() in nwbindry.h.
*/
struct XDATA {
uint8 object_id[4];
uint8 object_type[2];
uint8 object_name[48];
uint8 object_flag;
uint8 object_security;
uint8 object_has_properties;
} *xdata = (struct XDATA*) responsedata;
uint32 last_obj_id = GET_BE32(rdata);
uint8 *p = rdata+4;
int result;
NETOBJ obj;
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)(p+3),(int) *(p+2));
upstr(obj.name);
result = scan_for_obj(&obj, last_obj_id, 0);
if (!result){
U32_TO_BE32(obj.id, xdata->object_id);
U16_TO_BE16(obj.type, xdata->object_type);
strncpy(xdata->object_name, obj.name, 48);
xdata->object_flag = obj.flags;
xdata->object_security = obj.security;
if (nw_obj_has_prop(&obj) > 0)
xdata->object_has_properties = 0xff;
else xdata->object_has_properties = 0;
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
}
break;
case 0x38 : { /* change Bindery Objekt Security */
/*
* SDK 23/56 / wire 0x38 Change Bindery Object Security.
* Request payload after SubFunctionCode:
* byte NewSecurity
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Parser comparison: matches the documented layout.
* The implementation additionally enforces local SU
* rights before calling nw_change_obj_security().
* Include cross-check: NWChangeObjectSecurity() in
* nwbindry.h.
*/
/* only SU ! */
int result= -0xff;
if (act_c->id_flags&1) {
uint8 *p = rdata;
NETOBJ obj;
obj.type = GET_BE16(p+1);
xstrmaxcpy(obj.name, (char*)(p+4), (int) *(p+3));
result = nw_change_obj_security(&obj, (int)*p);
}
if (result < 0) completition = (uint8) -result;
} break;
case 0x39 : { /* create Property */
/*
* SDK 23/57 / wire 0x39 Create Property.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte PropertyFlags
* byte PropertySecurity
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* Parser comparison: matches the documented field
* order and ObjectType byte order.
* Include/WebSDK cross-check: NWCreateProperty() in
* nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+=2);
uint8 *object_name = ++p;
int prop_flags = (int) *(p+=object_namlen);
int prop_security = (int) *(++p);
int prop_namlen = (int) *(++p);
uint8 *prop_name = ++p;
int result = nw_create_prop( object_type,
object_name, object_namlen,
prop_name, prop_namlen,
prop_flags, prop_security);
if (result) completition = (uint8) -result;
} break;
case 0x3a : { /* delete property */
/*
* SDK 23/58 / wire 0x3a Delete Property.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* Parser comparison: matches the documented layout.
* Include/WebSDK cross-check: NWDeleteProperty() in
* nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+2);
uint8 *object_name= p+3;
int prop_namlen = (int) *(p+3+object_namlen);
uint8 *prop_name = p+4+object_namlen;
int result = nw_delete_property( object_type,
object_name, object_namlen,
prop_name, prop_namlen);
if (result < 0) completition = (uint8) -result;
} break;
case 0x3b : { /* Change Prop Security */
/*
* SDK 23/59 / wire 0x3b Change Property Security.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte NewPropertySecurity
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* Parser comparison: matches the documented layout.
* Include/WebSDK cross-check:
* NWChangePropertySecurity() in nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+=2);
uint8 *object_name= ++p;
int prop_security = (int) *(p+=object_namlen);
int prop_namlen = (int) *(++p);
uint8 *prop_name = ++p;
int result = nw_change_prop_security(object_type,
object_name, object_namlen,
prop_name, prop_namlen, prop_security);
if (result) completition = (uint8) -result;
} break;
case 0x3c : { /* Scan Property */
/*
* SDK 23/60 / wire 0x3c Scan Property.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* long LastInstance (Hi-Lo)
* byte PropertyNameLen
* byte SearchPropertyName[PropertyNameLen]
* Reply: PropertyName[16], flags, security,
* SearchInstance (Hi-Lo), ValueAvailable, and
* MoreProperties. Parser/reply comparison matches
* the documented layout.
* Include/WebSDK cross-check: NWScanProperty() in
* nwbindry.h.
*/
struct XDATA {
uint8 prop_name[16];
uint8 flags; /* set=2, dynamic=1 */
uint8 security;
uint8 akt_scan[4];
uint8 has_value; /* ff, if there are Prop's Values */
uint8 weisnicht;
} *xdata = (struct XDATA*) responsedata;
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+2);
uint8 *object_name = (p+=3);
uint32 last_scan = GET_BE32((p+object_namlen));
uint8 prop_namlen = (int)* (p+=object_namlen+4);
uint8 *prop_name = ++p;
NETPROP prop;
int result = nw_scan_property(&prop,
object_type, object_name, object_namlen,
prop_name, prop_namlen, &last_scan);
if (result > -1) {
strncpy(xdata->prop_name,
prop.name, sizeof(xdata->prop_name));
U32_TO_BE32(last_scan, xdata->akt_scan);
xdata->flags = prop.flags;
xdata->security = prop.security;
xdata->has_value = (uint8) result;
xdata->weisnicht = 0x0;
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
} break;
case 0x3d : { /* read Bindery Property Value */
/*
* SDK 23/61 / wire 0x3d Read Property Value.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte SegmentNumber
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* Reply: PropertyValue[128], MoreFlag, and
* PropertyFlags. Parser/reply comparison matches the
* documented layout.
* Include/WebSDK cross-check: NWReadPropertyValue() in
* nwbindry.h.
*/
struct XDATA {
uint8 property_value[128];
uint8 more_segments;
uint8 property_flags;
} *xdata = (struct XDATA*) responsedata;
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+2);
uint8 *object_name= p+3;
int segment_nr = (int) *(p+3+object_namlen);
int prop_namlen = (int) *(p+4+object_namlen);
uint8 *prop_name = p+5+object_namlen;
int result = nw_get_prop_val( object_type,
object_name, object_namlen,
segment_nr,
prop_name, prop_namlen,
xdata->property_value,
&(xdata->more_segments),
&(xdata->property_flags));
if (!result){
data_len = sizeof(struct XDATA);
} else completition = (uint8) -result;
} break;
case 0x3e : { /* write Bindery Property Value */
/*
* SDK 23/62 / wire 0x3e Write Property Value.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte SegmentNumber
* byte MoreFlag
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* byte PropertyValue[128]
* Parser comparison: matches the documented field
* order. The local variable is named erase_segment,
* but it consumes the SDK MoreFlag byte.
* Include/WebSDK cross-check: NWWritePropertyValue()
* in nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+2);
uint8 *object_name= p+3;
int segment_nr = (int) *(p+3+object_namlen);
int erase_segment = (int) *(p+4+object_namlen);
int prop_namlen = (int) *(p+5+object_namlen);
uint8 *prop_name = p+6+object_namlen;
uint8 *valdata = p+6+object_namlen+prop_namlen;
int result = nw_write_prop_value( object_type,
object_name, object_namlen,
segment_nr, erase_segment,
prop_name, prop_namlen,
valdata);
if (result) completition = (uint8) -result;
} break;
#if 0
case 0x3f : { /* Verify Bindery Object Password */
/*
* SDK 23/63 / wire 0x3f Verify Bindery Object
* Password.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte PasswordLen
* byte Password[PasswordLen]
* Not implemented yet; keep this disabled legacy
* compatibility stub next to the surrounding bindery
* password calls.
*/
break;
}
#endif
case 0x40: { /* change object password */
/*
* SDK 23/64 / wire 0x40 Change Bindery Object
* Password.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte OldPasswordLen
* byte OldPassword[OldPasswordLen]
* byte NewPasswordLen
* byte NewPassword[NewPasswordLen]
* Parser comparison: matches the WebSDK/include API
* shape (NWChangeObjectPassword). The local
* NDK/Core-Protocols PDF section has a transposed
* table that resembles set-member fields; do not use
* that table to add member/property parsing here.
*/
if (password_scheme & PW_SCHEME_CHANGE_PW) {
uint8 *p = rdata;
uint8 oldpassword[50];
uint8 newpassword[50];
NETOBJ obj;
int result;
obj.type = GET_BE16(p);
p+=2;
xstrmaxcpy(obj.name, p+1, (int) *p);
upstr(obj.name);
p += ((*p)+1);
xstrmaxcpy(oldpassword, p+1, (int) *p);
if (*p) memset(p+1, 0, *p);
p += ((*p)+1);
xstrmaxcpy(newpassword, p+1, (int) *p);
if (*p) memset(p+1, 0, *p);
if (0 == (result = find_obj_id(&obj))) {
XDPRINTF((6, 0, "CHPW: OLD=`%s`, NEW=`%s`", oldpassword,
newpassword));
internal_act=1;
if ((act_c->id_flags&1) ||
(0 == (result=test_allow_password_change(act_c->object_id))
&& /* PaPr,mst:25-Apr-00 */
0 == (result=nw_valid_newpasswd(act_c->object_id,newpassword))
&&
0 == (result=nw_test_unenpasswd(obj.id, oldpassword)))){
if ( (!(act_c->id_flags&1))
|| *newpassword
|| !(password_scheme & PW_SCHEME_LOGIN))
result=nw_set_passwd(obj.id, newpassword, 0);
else result = -0xff;
}
internal_act=0;
}
if (result < 0) completition = (uint8) -result;
memset(oldpassword, 0, 50);
memset(newpassword, 0, 50);
} else {
XDPRINTF((1, 0, "Change object password unencryted not enabled"));
completition=0xff;
}
} break;
case 0x41 : { /* add Bindery Object to Set */
/*
* SDK 23/65 / wire 0x41 Add Bindery Object To Set.
* Request payload after SubFunctionCode:
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* word MemberType (Hi-Lo)
* byte MemberNameLen
* byte MemberName[MemberNameLen]
* Parser comparison: matches the documented layout.
* Include/WebSDK cross-check: NWAddObjectToSet() in
* nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+=2);
uint8 *object_name= ++p;
int prop_namlen = (int) *(p+=object_namlen);
uint8 *prop_name = ++p;
int member_type = GET_BE16(p+prop_namlen);
int member_namlen = (int) *(p+=(prop_namlen+2));
uint8 *member_name= ++p;
int result = nw_add_obj_to_set( object_type,
object_name, object_namlen,
prop_name, prop_namlen,
member_type,
member_name, member_namlen);
if (result) completition = (uint8) -result;
} break;
case 0x42 : { /* delete Bindery Object from Set */
/*
* SDK 23/66 / wire 0x42 Delete Bindery Object From
* Set. Request payload is the same object/property/
* member tuple used by SDK 23/65 / wire 0x41.
* Parser comparison: matches the documented layout.
* Include/WebSDK cross-check: NWDeleteObjectFromSet()
* in nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+=2);
uint8 *object_name= ++p;
int prop_namlen = (int) *(p+=object_namlen);
uint8 *prop_name = ++p;
int member_type = GET_BE16(p+prop_namlen);
int member_namlen = (int) *(p+=(prop_namlen+2));
uint8 *member_name= ++p;
int result = nw_delete_obj_from_set( object_type,
object_name, object_namlen,
prop_name, prop_namlen,
member_type,
member_name, member_namlen);
if (result) completition = (uint8) -result;
} break;
case 0x43 : { /* is Bindery Object in Set */
/*
* SDK 23/67 / wire 0x43 Is Bindery Object In Set.
* Request payload is the same object/property/member
* tuple used by SDK 23/65 and SDK 23/66.
* Parser comparison: matches the documented layout.
* Include/WebSDK cross-check: NWIsObjectInSet() in
* nwbindry.h.
*/
uint8 *p = rdata;
int object_type = GET_BE16(p);
int object_namlen = (int) *(p+=2);
uint8 *object_name= ++p;
int prop_namlen = (int) *(p+=object_namlen);
uint8 *prop_name = ++p;
int member_type = GET_BE16(p+prop_namlen);
int member_namlen = (int) *(p+=(prop_namlen+2));
uint8 *member_name= ++p;
int result = nw_is_obj_in_set( object_type,
object_name, object_namlen,
prop_name, prop_namlen,
member_type,
member_name, member_namlen);
if (result) completition = (uint8) -result;
} break;
case 0x44 : { /* CLOSE BINDERY */
/*
* SDK 23/68 / wire 0x44 Close Bindery.
* Request payload after SubFunctionCode: none.
* Parser comparison: matches the documented layout.
* MARS-NWE treats this as a successful no-op.
*/
;
} break;
case 0x45 : { /* OPEN BINDERY */
/*
* SDK 23/69 / wire 0x45 Open Bindery.
* Request payload after SubFunctionCode: none.
* Parser comparison: matches the documented layout.
* MARS-NWE treats this as a successful no-op.
*/
;
} break;
case 0x46 : { /* GET BINDERY ACCES LEVEL */
/*
* SDK 23/70 / wire 0x46 Get Bindery Access Level.
* Request payload after SubFunctionCode: none.
* Reply: SecurityAccessLevel byte and LoggedObjectID
* long (Hi-Lo). Parser/reply comparison matches the
* documented layout.
* Include/WebSDK cross-check: NWGetBinderyAccessLevel()
* in nwbindry.h.
*/
#if 0
struct XDATA {
uint8 acces_level;
uint8 object_id[4];
}
#endif
uint8 *xdata = responsedata;
if (!act_c->object_id) {
*xdata = (uint8) 0;
memset(xdata+1, 0xff, 4);
} else {
*xdata = (act_c->id_flags&1) ? (uint8) 0x33
: (uint8) 0x22;
U32_TO_BE32(act_c->object_id, (xdata+1));
}
data_len = 5;
XDPRINTF((2,0, "ACCESS LEVEL:=0x%x, obj=0x%lx",
(int) *xdata, act_c->object_id));
}
break;
#if 0
case 0x47 : /* SDK 23/71 / wire 0x47 Scan Bindery Object Trustee
* Paths is handled locally in nwconn.c because the call
* resolves directory trustee paths rather than bindery
* records alone. */
#endif
case 0x48 : { /* GET BINDERY ACCES LEVEL from OBJECT ??? */
/*
* SDK 23/72 / wire 0x48 Get Bindery Object Access
* Level.
* Request payload after SubFunctionCode:
* long ObjectID (Hi-Lo)
* Reply: ObjectAccessLevel byte. Parser/reply
* comparison matches the documented layout.
*/
struct XDATA {
uint8 acces_level;
} *xdata = (struct XDATA*) responsedata;
NETOBJ obj;
int result;
obj.id = GET_BE32(rdata);
result = nw_get_obj(&obj);
if (!result) {
/* don't know whether this is ok ?? */
if (act_c->id_flags&1) {
xdata->acces_level = 0x33;
} else if (act_c->object_id == obj.id) {
xdata->acces_level = 0x22;
} else {
xdata->acces_level = 0x11;
}
data_len = sizeof(struct XDATA);
} else completition = (uint8)-result;
}
break;
case 0x49 : { /* IS CALLING STATION A MANAGER */
/*
* SDK 23/73 / wire 0x49 Is Calling Station A Manager.
* Request payload after SubFunctionCode: none.
* Return code indicates manager/non-manager status.
* Parser comparison matches the documented layout.
*/
completition = (act_c->id_flags&1) ? 0 : 0xff;
/* here only SU = Manager */
/* not manager, then completition = 0xff */
}
break;
case 0x4a : { /* keyed verify password */
/*
* SDK 23/74 / wire 0x4a Keyed Verify Password.
* Request payload after SubFunctionCode:
* byte Key[8]
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* Parser comparison: matches the documented layout;
* the implementation skips the 8-byte key before
* reading ObjectType/ObjectName.
*/
uint8 *p = rdata+sizeof(act_c->crypt_key);
NETOBJ obj;
int result;
obj.type = GET_BE16(p);
strmaxcpy((char*)obj.name, (char*)(p+3), *(p+2));
upstr(obj.name);
if (0 == (result = find_obj_id(&obj))) {
internal_act = 1;
result=nw_test_passwd(obj.id, act_c->crypt_key, rdata);
internal_act = 0;
}
if (result < 0) completition = (uint8) -result;
XDPRINTF((2,0, "Keyed Verify PW from OBJECT='%s', type=0x%x, result=%d",
obj.name, obj.type, result));
}
break;
case 0x4b : { /* keyed change pasword */
/*
* SDK 23/75 / wire 0x4b Keyed Change Password.
* Request payload after SubFunctionCode:
* byte Key[8]
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte NewPasswordLen
* byte NewPassword[NewPasswordLen]
* Parser comparison: matches the documented layout;
* the implementation skips the 8-byte key before
* reading ObjectType/ObjectName/NewPassword.
*/
uint8 *p = rdata+sizeof(act_c->crypt_key);
NETOBJ obj;
int result;
obj.type = GET_BE16(p);
p+=2;
strmaxcpy((char*)obj.name, (char*)(p+1), *p);
upstr(obj.name);
/* from Guntram Blohm */
p += (*p+1); /* here is crypted password length */
if (0 == (result = find_obj_id(&obj))) {
internal_act=1;
if (obj.id != 1)
result=test_allow_password_change(obj.id);
if (!result)
result=nw_keychange_passwd(obj.id, act_c->crypt_key,
rdata, (int)*p, p+1, act_c->id_flags);
if (!result) test_ins_unx_user(obj.id);
internal_act = 0;
}
if (result< 0) completition = (uint8) -result;
XDPRINTF((2, 0, "Keyed Change PW from OBJECT='%s', type=0x%x, result=0x%x",
obj.name, obj.type, result));
}
break;
case 0x4c : { /* List Relations of an Object */
/*
* SDK 23/76 / wire 0x4c List Relations Of An Object.
* Request payload after SubFunctionCode:
* long LastSeen (Hi-Lo)
* word ObjectType (Hi-Lo)
* byte ObjectNameLen
* byte ObjectName[ObjectNameLen]
* byte PropertyNameLen
* byte PropertyName[PropertyNameLen]
* Reply: Count word (Hi-Lo) followed by up to 32
* relation object IDs as long (Hi-Lo).
* Parser/reply comparison matches the documented
* layout.
*/
struct XDATA {
uint8 count[2];
uint8 relations[32*4];
} *xdata = (struct XDATA*) responsedata;
uint8 *p = rdata;
uint32 last_seen = GET_BE32(p);
int object_type = GET_BE16(p+4);
int object_namlen = (int)*(p+6);
uint8 *object_name = p+7;
int prop_namlen = (int)*(p+7+object_namlen);
uint8 *prop_name = p+8+object_namlen;
uint32 relations[32];
int result = nw_list_obj_relations(object_type,
object_name, object_namlen,
prop_name, prop_namlen,
last_seen, relations, 32);
if (result > -1) {
int k;
uint8 *rp = xdata->relations;
U16_TO_BE16((uint16)result, xdata->count);
for (k=0; k < result; k++) {
U32_TO_BE32(relations[k], rp);
rp += 4;
}
data_len = 2 + (result * 4);
XDPRINTF((2, 0, "List Relations of an Object count=%d", result));
} else completition = (uint8)-result;
} break;
case 0x64 : { /* Create Queue, prehandled by nwconn */
/*
* SDK 23/100 / wire 0x64 Create Queue.
* Request payload after SubFunctionCode:
* word QueueType (Hi-Lo)
* byte QueueNameLen
* byte QueueName[QueueNameLen]
* byte PathBase
* byte PathLen
* byte PathName[PathLen]
* Reply: QueueID long (Hi-Lo).
* Handoff comparison: nwconn.c resolves PathBase/PathName
* to a full path, rewrites PathBase to 0, and forwards the
* adjusted payload here. The parser then consumes the
* documented QueueType/QueueName/full-path layout.
*/
int q_typ = GET_BE16(rdata);
int q_name_len = *(rdata+2);
uint8 *q_name = rdata+3;
/* inserted by nwconn !!! */
#if 0
int dummy = *(rdata+3+q_name_len);
#endif
int pathlen = *(rdata+3+q_name_len+1);
uint8 *path = rdata+3+q_name_len+2;
uint32 q_id;
int result = nw_creat_queue(q_typ,
q_name, q_name_len,
path, pathlen, &q_id);
if (result > -1) {
U32_TO_BE32(q_id, responsedata);
data_len=4;
} else
completition=(uint8)(-result);
} break;
case 0x65 : { /* Destroy Queue */
/*
* SDK 23/101 / wire 0x65 Destroy Queue.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply: no data.
* Parser comparison matches the documented layout.
*/
uint32 q_id = GET_BE32(rdata);
int result=-0xd3; /* no rights */
if (act_c->id_flags&1)
result=nw_destroy_queue(q_id);
if (result < 0)
completition=(uint8)(-result);
} break;
case 0x66 : { /* Read Queue Current Status,old */
/*
* SDK 23/102 / wire 0x66 Read Queue Current Status (old).
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply:
* long QueueID (Hi-Lo)
* byte QueueStatus
* byte CurrentEntries
* byte CurrentServers
* long ServerIDList[CurrentServers] (Hi-Lo)
* byte ServerStationList[CurrentServers]
* Parser/reply comparison matches the documented old
* queue-status layout.
*/
struct XDATA {
uint8 id[4];
uint8 status;
uint8 entries;
uint8 servers;
uint8 data[1];
} *xdata = (struct XDATA*) responsedata;
uint32 q_id = GET_BE32(rdata);
int status;
int entries;
int servers;
int server_ids[25];
int server_conns[25];
int result=nw_get_queue_status(q_id, &status, &entries,
&servers, server_ids, server_conns);
if (result>-1) {
int k;
uint8 *p=&(xdata->data[0]);
U32_TO_BE32(q_id, xdata->id);
xdata->status=status;
xdata->entries=entries;
xdata->servers=servers;
k=-1;
while (++k < servers) {
U32_TO_BE32(server_ids[k], p);
p+=4;
}
k=-1;
while (++k < servers) {
*p=(uint8) server_conns[k];
++p;
}
data_len=sizeof(struct XDATA)-1+5*servers;
} else
completition=(uint8)-result;
}
break;
case 0x67 : /* Set Queue Current Status,old */
case 0x7e : { /* Set Queue Current Status */
/*
* SDK 23/103 / wire 0x67 Set Queue Current Status (old)
* and SDK 23/126 / wire 0x7e Set Queue Current Status.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* byte QueueStatus for old wire 0x67
* long QueueStatus (Lo-Hi) for newer wire 0x7e
* Reply: no data.
* Parser comparison: the shared implementation currently
* consumes only one status byte for both variants; keep as
* compatibility behavior until a direct requester test
* proves whether wire 0x7e needs the full long field.
*/
uint32 q_id = GET_BE32(rdata);
int status = *(rdata+4);
int result =
(act_c->id_flags&1) /* TODO BETTER */
? nw_set_queue_status(q_id, status)
: -0xd3; /* no rights */
if (result< 0)
completition=(uint8)-result;
/* NO REPLY */
}
break;
case 0x6A : /* Remove Job from Queue OLD */
case 0x80 : { /* Remove Job from Queue NEW */
/*
* SDK 23/106 / wire 0x6a Remove Job From Queue (old)
* and SDK 23/128 / wire 0x80 Remove Job From Queue.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x6a
* long JobNumber (Lo-Hi) for newer wire 0x80
* Reply: no data.
* Parser comparison: both variants currently consume a
* 16-bit JobNumber.
*/
uint32 q_id = GET_BE32(rdata);
uint32 job_id = (ufunc == 0x6A)
? GET_BE16(rdata+4)
: GET_BE16(rdata+4);
int result=nw_remove_job_from_queue(
act_c->object_id,
q_id, job_id);
if (result < 0)
completition=(uint8)-result;
}
break;
case 0x6B : { /* Get Queue Job List, old */
/*
* SDK 23/107 / wire 0x6b Get Queue Job List (old).
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply:
* word JobCount (Hi-Lo)
* word JobNumber[JobCount] (Hi-Lo)
* Parser/reply comparison is delegated to the queue backend
* helper and matches the old queue job-list shape.
*/
uint32 q_id=GET_BE32(rdata);
int result=nw_get_queue_job_list_old(q_id, responsedata);
if (result > -1)
data_len=result;
else
completition=(uint8)-result;
}
break;
case 0x68: /* creat queue job and file old */
case 0x79: { /* creat queue job and file new */
/*
* SDK 23/104 / wire 0x68 Create Queue Job And File (old)
* and SDK 23/121 / wire 0x79 Create Queue Job And File.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* JobStruct, 256 bytes for old wire 0x68 and extended
* size for newer wire 0x79
* Reply: completed JobStruct including job number and file
* handle.
* Handoff comparison: nwconn.c returns -2 so nwbind.c can
* perform the queue-job file prehandling before this parser
* consumes QueueID and the JobStruct.
*/
uint32 q_id = GET_BE32(rdata);
uint8 *q_job = rdata+4; /* jobsize = 256(old) or 280 */
int result = nw_creat_queue_job(
act_connection, ncprequest->task,
act_c->object_id,
q_id, q_job,
responsedata,
ufunc==0x68);
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
/*0xd3 err no queue rights */
}
break;
case 0x6C: /* Get Queue Job Entry old */
case 0x7A: { /* Read Queue Job Entry */
/*
* SDK 23/108 / wire 0x6c Read Queue Job Entry (old)
* and SDK 23/122 / wire 0x7a Read Queue Job Entry.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x6c
* long JobNumber (Lo-Hi) for newer wire 0x7a
* Reply: JobStruct.
* Handoff comparison: nwconn.c inserts the queue job file
* handle before forwarding here. Both variants currently
* consume a 16-bit JobNumber.
*/
uint32 q_id = GET_BE32(rdata);
int job_id = GET_BE16(rdata+4);
/* added by nwconn */
uint32 fhandle = GET_BE32(rdata+8);
int result=nw_get_q_job_entry(q_id, job_id, fhandle,
responsedata, ufunc==0x6c);
if (result > -1)
data_len=result;
else completition=(uint8)-result;
}
break;
case 0x69: /* close file and start queue old ?? */
case 0x7f: { /* close file and start queue */
/*
* SDK 23/105 / wire 0x69 Close File And Start Queue Job
* (old) and SDK 23/127 / wire 0x7f Close File And Start
* Queue Job.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Lo-Hi in the old PDF table)
* Reply: no data for the old call; the backend may return
* compatibility data for the newer call.
* Parser comparison: both variants currently consume a
* 16-bit JobNumber with GET_BE16().
*/
uint32 q_id = GET_BE32(rdata);
uint32 job_id = (ufunc==0x69)
? GET_BE16(rdata+4)
: GET_BE16(rdata+4);
int result = nw_close_queue_job(q_id, job_id,
responsedata);
if (result > -1)
data_len=result;
else
completition = (uint8) -result;
}
break;
case 0x6f : { /* attach server to queue */
/*
* SDK 23/111 / wire 0x6f Attach Queue Server To Queue.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply: no data.
* Parser comparison matches the documented queue-server
* attach layout.
*/
uint32 q_id = GET_BE32(rdata);
int result=nw_attach_server_to_queue(
act_c->object_id,
act_connection,
q_id);
if (result < 0)
completition = (uint8) -result;
/* NO REPLY */
}
break;
case 0x70 : { /* detach server from queue */
/*
* SDK 23/112 / wire 0x70 Detach Queue Server From Queue.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply: no data.
* Parser comparison matches the documented queue-server
* detach layout.
*/
uint32 q_id = GET_BE32(rdata);
int result=nw_detach_server_from_queue(
act_c->object_id,
act_connection,
q_id);
if (result < 0)
completition = (uint8) -result;
/* NO REPLY */
}
break;
#if 0
case 0x74: /* Change To Client Rights (old) */
case 0x85: /* Change To Client Rights */
{
/*
* SDK 23/116 / wire 0x74 Change To Client Rights (old)
* and SDK 23/133 / wire 0x85 Change To Client Rights.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x74
* long JobNumber (Lo-Hi) for newer wire 0x85
* Reply: no data.
* This is a queue-server identity switch: the server
* connection temporarily assumes the job owner's security
* equivalence and recalculates directory rights. The
* current code has no implementation for that connection
* identity transition, so the audited endpoint remains a
* disabled future stub.
*/
}
break;
case 0x75: { /* Restore Queue Server Rights */
/*
* SDK 23/117 / wire 0x75 Restore Queue Server Rights.
* Request payload after SubFunctionCode: none.
* Reply: no data.
* This must reverse SDK 23/116 or SDK 23/133 by restoring
* the queue server's original login identity and security
* equivalence list. It is disabled until the corresponding
* Change To Client Rights transition exists.
*/
}
break;
case 0x76: /* Read Queue Server Current Status (old) */
case 0x86: /* Read Queue Server Current Status */
{
/*
* SDK 23/118 / wire 0x76 Read Queue Server Current Status
* (old) and SDK 23/134 / wire 0x86 Read Queue Server
* Current Status.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* long ServerID (Hi-Lo)
* byte ServerStation for old wire 0x76
* long ServerStation (Lo-Hi) for newer wire 0x86
* Reply: byte ServerStatusRecord[64].
* The queue backend currently tracks attached servers well
* enough for queue status lists, but does not maintain this
* per-server 64-byte opaque status record.
*/
}
break;
case 0x77: { /* Set Queue Server Current Status */
/*
* SDK 23/119 / wire 0x77 Set Queue Server Current Status.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* byte ServerStatusRecord[64]
* Reply: no data.
* The 64-byte record is opaque to the queue manager, but it
* must be stored per attached queue server before the read
* status calls above can be implemented.
*/
}
break;
#endif
case 0x78: /* Get Queue Job File Size (old) */
case 0x87: /* Get Queue Job File Size */
{
/*
* SDK 23/120 / wire 0x78 Get Queue Job File Size (old)
* and SDK 23/135 / wire 0x87 Get Queue Job File Size.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x78
* long JobNumber for newer wire 0x87
* Reply documented by the WebSDK queue pages:
* long QueueID, long JobNumber, long FileSize.
* Parser/reply comparison: both variants currently consume
* a 16-bit JobNumber. The old reply returns a 16-bit
* JobNumber, and the newer reply pads that 16-bit value to
* four bytes instead of serializing a true long.
*/
uint32 q_id = GET_BE32(rdata);
uint32 job_id = (ufunc==0x78)
? GET_BE16(rdata+4)
: GET_BE16(rdata+4);
int result = nw_get_queue_job_file_size(q_id, job_id);
if (result > -1) {
uint8 *p=responsedata;
U32_TO_BE32(q_id, p); p+=4;
if (ufunc==0x78) {
U16_TO_BE16(job_id, p); p+=2;
} else {
/* U32_TO_BE32(job_id, p); p+=4; */
U16_TO_BE16(job_id, p); p+=2;
*(p++)=0;
*(p++)=0;
}
U32_TO_BE32(result, p); p+=4;
data_len=(int)(p-responsedata);
} else
completition = (uint8) -result;
}
break;
case 0x71 : /* service queue job old */
case 0x7c : { /* service queue job */
/*
* SDK 23/113 / wire 0x71 Service Queue Job (old) and
* SDK 23/124 / wire 0x7c Service Queue Job.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word TargetServiceType (Hi-Lo)
* Reply: QueueJobStruct, old 54-byte fixed reply for wire
* 0x71 and newer reserved-prefix/NWQueueJobStruct reply for
* wire 0x7c, including the opened job file handle.
* Parser comparison matches the common QueueID/type request
* header and delegates the variant reply shape to the queue
* backend helper.
*/
uint32 q_id = GET_BE32(rdata);
int type = GET_BE16(rdata+4);
int result=nw_service_queue_job(
act_c->object_id,
act_connection, ncprequest->task,
q_id, type, responsedata,
ufunc==0x71 );
if (result > -1)
data_len=result;
else
completition=(uint8)-result;
}
break;
#if 1
case 0x6D: /* Change Queue Job Entry old */
case 0x7B: /* Change Queue Job Entry */
{
/*
* SDK 23/109 / wire 0x6d Change Queue Job Entry (old) and
* SDK 23/123 / wire 0x7b Change Queue Job Entry.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* QueueJobStruct, 256 bytes for old wire 0x6d and 280
* bytes for newer wire 0x7b.
* Reply: no data.
* Parser comparison matches the documented QueueID plus
* variant job-structure layout and delegates the structure
* copy to the queue backend helper.
*/
uint32 q_id = GET_BE32(rdata);
int result = nw_change_queue_job_entry(act_c->object_id,
q_id, rdata+4,
ufunc==0x6D);
if (result < 0)
completition=(uint8)-result;
}
break;
#endif
#if 0
case 0x6e: { /* Change Queue Job Position (old) */
/*
* SDK 23/110 / wire 0x6e Change Queue Job Position.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo)
* byte NewPosition
* Reply: no data.
* NetWare documents this as a 2.x/3.x queue-management
* call, but MARS-NWE currently has no queue helper that
* reorders job entries. Keep this as a disabled
* documentation stub until the queue backend can update
* every affected job position atomically.
*/
}
break;
#endif
case 0x7d : { /* Read Queue Current Status, new */
/*
* SDK 23/125 / wire 0x7d Read Queue Current Status.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* Reply:
* long QueueID (Hi-Lo)
* long QueueStatus
* long CurrentEntries
* long CurrentServers
* long ServerIDList[CurrentServers] (Hi-Lo)
* long ServerStationList[CurrentServers]
* Parser/reply comparison matches the documented new
* queue-status layout; scalar status/count/station fields use
* the existing 32-bit mars_nwe queue byte-order convention.
*/
struct XDATA {
uint8 id[4]; /* queue id */
uint8 status[4]; /* &1 no station allowed */
/* &2 no other queue server allowed */
/* &4 no queue server allowed get entries */
uint8 entries[4]; /* current entries */
uint8 servers[4]; /* current servers */
} *xdata = (struct XDATA*) responsedata;
uint32 q_id = GET_BE32(rdata);
int status;
int entries;
int servers;
int server_ids[25];
int server_conns[25];
int result=nw_get_queue_status(q_id, &status, &entries,
&servers, server_ids, server_conns);
if (result>-1) {
int k;
uint8 *p=responsedata+sizeof(*xdata);
U32_TO_BE32(q_id, xdata->id);
U32_TO_32(status, xdata->status);
U32_TO_32(entries, xdata->entries);
U32_TO_32(servers, xdata->servers);
k=-1;
while (++k < servers) {
U32_TO_BE32(server_ids[k], p);
p+=4;
}
k=-1;
while (++k < servers) {
U32_TO_32(server_conns[k], p);
p+=4;
}
data_len=sizeof(struct XDATA)+8*servers;
} else
completition=(uint8)-result;
} break;
case 0x81 : { /* Get Queue Job List */
/*
* SDK 23/129 / wire 0x81 Get Queue Job List.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* long QueueStartPosition
* Reply:
* long TotalQueueJobs
* long ReplyQueueJobNumbers
* long JobNumberList[ReplyQueueJobNumbers]
* Parser comparison matches the documented request header
* and delegates the paged long-list reply to the queue backend
* helper.
*/
uint32 q_id = GET_BE32(rdata);
uint32 offset = GET_BE32(rdata+4);
#if 0
struct XDATA {
uint8 total_jobs[4];
uint8 reply_numbers[4]; /* max. 125 replies */
uint8 job_list[4]; /* this is repeated */
} *xdata = (struct XDATA*) responsedata;
#endif
int result=nw_get_queue_job_list(q_id, offset, responsedata);
if (result > -1)
data_len=result;
else
completition=(uint8)-result;
}break;
#if 0
case 0x82: { /* Change Job Priority */
/*
* SDK 23/130 / wire 0x82 Change Job Priority.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* long JobNumber (Lo-Hi)
* long Priority (Lo-Hi)
* Reply: no data.
* This is not the older SDK 23/110 / wire 0x6e Change
* Queue Job Position byte-position call; it changes the
* long priority field in the newer queue job structure.
* The queue backend does not expose a priority update helper
* yet, so keep the endpoint documented as a disabled future
* stub.
*/
}
break;
#endif
case 0x72: /* finish servicing queue job (old)*/
case 0x83: { /* finish servicing queue job */
/*
* SDK 23/114 / wire 0x72 Finish Servicing Queue Job (old)
* and SDK 23/131 / wire 0x83 Finish Servicing Queue Job.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x72
* long JobNumber plus long ChargeInfo for newer wire 0x83
* Reply: no data.
* Parser comparison: both variants currently consume a
* 16-bit JobNumber, and the newer ChargeInfo field is
* ignored.
*/
uint32 q_id = GET_BE32(rdata);
uint32 job_id = GET_BE16(rdata+4);
#if 0
uint32 chargeinfo = GET_BE32(rdata+8);
#endif
int result = nw_finish_abort_queue_job(0,
act_c->object_id,
act_connection,
q_id, job_id);
if (result <0)
completition=(uint8) -result;
}break;
#if 0
case 0x96: { /* Get Current Account Status */
/*
* SDK 23/150 / wire 0x96 Get Current Account Status.
* NetWare Server: 2.x, 3.x, 4.x, 5.x.
* Eligible for the current 1.x/2.x/3.x compatibility
* audit because accounting servers and print servers can
* query ACCOUNT_BALANCE/ACCOUNT_HOLDS before charging for
* services.
* Request payload after SubFunctionCode:
* word ClientType (Hi-Lo)
* byte ClientNameLen
* byte ClientName[ClientNameLen]
* Reply payload:
* long AccountBalance (Hi-Lo)
* long CreditLimit (Hi-Lo)
* byte Reserved[120]
* 16 x { long HolderID (Hi-Lo), long HoldAmount (Hi-Lo) }
* Source outcome: no accounting backend exists yet. Keep
* this as a disabled future stub until bindery-backed or
* libdirectory-backed account properties and hold records
* exist.
* Future owner: accounting provider, with possible print
* queue integration for page/job charging.
*/
}
break;
case 0x97: { /* Submit Account Charge */
/*
* SDK 23/151 / wire 0x97 Submit Account Charge.
* NetWare Server: 2.x, 3.x, 4.x, 5.x.
* Request payload after SubFunctionCode:
* long ChargeAmount (Hi-Lo)
* long HoldCancelAmount (Hi-Lo)
* word ServiceType (Hi-Lo)
* word ClientType (Hi-Lo)
* word CommentType (Hi-Lo)
* byte ClientNameLen
* byte ClientName[ClientNameLen]
* byte CommentLen
* byte Comment[CommentLen]
* Reply: no data. Completion reports accounting result.
* Source outcome: no accounting charge/audit-file writer is
* present. Keep disabled; do not fake success because print
* servers use this to charge completed work.
*/
}
break;
case 0x98: { /* Submit Account Hold */
/*
* SDK 23/152 / wire 0x98 Submit Account Hold.
* NetWare Server: 2.x, 3.x, 4.x, 5.x.
* Request payload after SubFunctionCode:
* long Amount (Hi-Lo)
* word ClientType (Hi-Lo)
* byte ClientNameLen
* byte ClientName[ClientNameLen]
* Reply: no data. Completion reports accounting result.
* Source outcome: no ACCOUNT_HOLDS storage/update logic is
* present. Keep disabled until an accounting provider can
* update holds atomically with account balance checks.
*/
}
break;
case 0x99: { /* Submit Account Note */
/*
* SDK 23/153 / wire 0x99 Submit Account Note.
* NetWare Server: 2.x, 3.x, 4.x, 5.x.
* Request payload after SubFunctionCode:
* word ServiceType (Hi-Lo)
* word ClientType (Hi-Lo)
* word CommentType (Hi-Lo)
* byte ClientNameLen
* byte ClientName[ClientNameLen]
* byte CommentLen
* byte Comment[CommentLen]
* Reply: no data.
* Source outcome: no NET$ACCT.DAT/audit-note writer exists
* yet. Keep disabled until the accounting provider defines
* durable note/audit behavior.
*/
}
break;
#endif
case 0x73: /* abort servicing queue job (old) */
case 0x84: { /* abort servicing queue job */
/*
* SDK 23/115 / wire 0x73 Abort Servicing Queue Job (old)
* and SDK 23/132 / wire 0x84 Abort Servicing Queue Job.
* Request payload after SubFunctionCode:
* long QueueID (Hi-Lo)
* word JobNumber (Hi-Lo) for old wire 0x73
* long JobNumber for newer wire 0x84
* Reply: no data for NCP 23; DOS-shell INT 21h wrappers
* expose a two-byte zero reply length.
* Parser/reply comparison: both variants currently consume
* a 16-bit JobNumber, and the success path emits a two-byte
* zero reply for compatibility with older clients.
*/
uint32 q_id = GET_BE32(rdata);
uint32 job_id = GET_BE16(rdata+4);
int result = nw_finish_abort_queue_job(1,
act_c->object_id,
act_connection,
q_id, job_id);
if (result <0)
completition=(uint8) -result;
else {
memset(responsedata, 0, 2);
data_len=2;
}
}break;
case 0xc8 : { /* Check Console Privileges */
/*
* SDK 23/200 / wire 0xc8 Check Console Privileges.
* Request payload after SubFunctionCode: none.
* Reply: no data.
* Completion: 0x00 if the connection has console
* operator privileges, otherwise 0xc6 No Console Rights.
*/
if (has_console_privileges(act_c)) {
XDPRINTF((2, 0, "Check Console Privileges user=0x%x result=0x0",
act_c->object_id));
} else {
XDPRINTF((1, 0, "Check Console Privileges user=0x%x result=0xc6",
act_c ? act_c->object_id : 0));
completition=0xc6;
}
} break;
case 0xc9 : { /* GET FILE SERVER DESCRIPTION STRINGs */
/*
* SDK 23/201 / wire 0xc9 Get File Server Description
* Strings.
* Request payload after SubFunctionCode: none.
* Reply: up to 512 bytes of NUL-terminated description
* strings: company, revision, revision date, copyright,
* followed by any implementation-specific tail strings.
*/
char *company = "www.disconnected-by-peer.at";
char *revision = "Mars NWE %d.%d.pl%d";
char *revision_date = MARS_NWE_BUILD_DATE;
char *copyright = "(C) Martin Stover and contributors";
int k=strlen(company)+1;
int l;
memset(responsedata, 0, 512);
strcpy(responsedata, company);
l = 1 + sprintf(responsedata+k, revision,
_VERS_H_, _VERS_L_, _VERS_P_ );
k += l;
strcpy(responsedata+k, revision_date);
k += (strlen(revision_date)+1);
strcpy(responsedata+k, copyright);
k += (strlen(copyright)+1);
data_len = k;
} break;
case 0xcd : { /* GET FILE SERVER LOGIN STATUS */
/*
* SDK 23/205 / wire 0xcd Get File Server Login Status.
* Request payload after SubFunctionCode: none.
* Reply: UserLoginAllowed. The PDF documents this as a
* long value, while the CLIB/WebSDK wrapper exposes a
* byte-sized login-enabled flag and the current reply keeps
* that one-byte compatibility shape.
*/
struct XDATA {
uint8 login_allowed; /* 0 NO , 1 YES */
} *xdata = (struct XDATA*) responsedata;
xdata->login_allowed = 1;
data_len = 1;
}
break;
case 0xd1 : /* Send Console Broadcast (old) */
{
/*
* SDK 23/209 / wire 0xd1 Send Console Broadcast (old).
* Request payload after SubFunctionCode:
* byte NumberOfStations
* byte StationList[NumberOfStations]
* byte MessageLen
* byte BroadcastMessage[MessageLen]
* The PDF table labels StationList as long[], but its
* SubFuncStrucLen formula is the old byte-list form and
* matches this parser. New SDK 23/253 / wire 0xfd uses
* 32-bit connection numbers and is not enabled here.
*/
uint8 *p = rdata;
int count_conns = (int) *p++;
uint8 *co = p;
int msglen = (int) *(p+count_conns);
char *msg = (char*) p+count_conns+1;
int k = -1;
if (count_conns) {
while (++k < count_conns) {
int conn= (int) *co++;
if (conn == act_connection) {
strmaxcpy(act_c->message, msg, min(58, msglen));
connect_status = 0x40; /* don't know why */
} else if (conn && --conn < max_connections) {
CONNECTION *cc= &(connections[conn]);
if (cc->object_id) { /* if logged */
strmaxcpy(cc->message, msg, min(58, msglen));
nwserv_handle_msg(conn+1);
}
}
}
} else {
strmaxcpy(act_c->message, msg, min(58, msglen));
connect_status = 0x40; /* don't know why */
}
}
break;
case 0xd2 : { /* Clear Connection Number (M@K) */
/*
* SDK 23/210 / wire 0xd2 Clear Connection Number
* (old).
* Request payload after SubFunctionCode:
* byte ConnectionNumber
* Reply: no data. SDK 23/254 / wire 0xfe uses a
* long ConnectionNumber and is not implemented in this
* dispatcher.
*/
/* 14-Jun-2003 by Marco Cavallini */
uint8 conxnumber = *rdata - 1 ; // pointer to NCPREQUEST structure (receive aligned to 1 and we must align to 0)
char buf[255] ;
CONNECTION *cn=&connections[conxnumber];
sprintf(buf, "(M@K) Clear Connection Number = %d <==========", conxnumber);
XDPRINTF((1, 0, buf));
if (cn->active) {
strmaxcpy(cn->message, "MARS_NWE has killed your connection", 58);
sprintf(buf, "(M@K) KILL = Conn=%d PID=%d", conxnumber, cn->pid_nwconn);
XDPRINTF((1, 0, buf));
nwserv_handle_msg(conxnumber+1); // send message to the client
if (cn->pid_nwconn > -1)
kill(cn->pid_nwconn, SIGTERM); // kill it using brute force (M@K)
}
} break;
case 0xd3 : { /* down File Server */
/*
* SDK 23/211 / wire 0xd3 Down File Server.
* Request payload after SubFunctionCode:
* byte ForceFlag
* Reply: no data. The documented ForceFlag is currently
* ignored; supervisor rights are required and the local
* server-down path is always requested on success.
*/
internal_act=1;
if (HAVE_SU_RIGHTS(act_c->object_id)) { /* only SUPERVISOR */
/* inform nwserv */
nwserv_down_server();
} else {
/* 16-May-99
* correct completition code from Paolo Prandini
*/
completition = 0xc6; /* not authorized */
}
internal_act=0;
}
break;
#if 0
case 0xca : /* SDK 23/202 / wire 0xca Set File Server Date And Time */
/*
* Request payload after SubFunctionCode: year/month/day/
* hour/minute/second bytes. Not wired to the MARS-NWE
* server clock.
*/
return(-1);
break;
case 0xcb : /* SDK 23/203 / wire 0xcb Disable File Server Login */
case 0xcc : /* SDK 23/204 / wire 0xcc Enable File Server Login */
/*
* Request payload after SubFunctionCode: none. These
* require console operator privileges and toggle the server
* login-enabled state that SDK 23/205 reports.
*/
return(-1);
break;
case 0xcf : /* SDK 23/207 / wire 0xcf Disable Transaction Tracking */
case 0xd0 : /* SDK 23/208 / wire 0xd0 Enable Transaction Tracking */
/*
* Request payload after SubFunctionCode: none. TTS is not
* modelled by the current compatibility backend.
*/
return(-1);
break;
case 0xd4 : /* SDK 23/212 / wire 0xd4 Get File System Statistics */
case 0xd5 : /* SDK 23/213 / wire 0xd5 Get Transaction Tracking Statistics */
case 0xd6 : /* SDK 23/214 / wire 0xd6 Read Disk Cache Statistics */
case 0xd7 : /* SDK 23/215 / wire 0xd7 Get Drive Mapping Table */
case 0xd8 : /* SDK 23/216 / wire 0xd8 Read Physical Disk Statistics */
case 0xd9 : /* SDK 23/217 / wire 0xd9 Get Disk Channel Statistics */
/*
* NetWare 2.x server-environment statistics block. The
* local NDK/Core Protocols PDF documents these as console-
* rights-protected queries returning uptime interval markers
* plus real filesystem, TTS, cache, drive-mapping, physical-
* disk, or disk-channel counters/tables. The uploaded
* WebSDK/include material exposes matching server-statistics
* APIs such as NWGetFileSystemStats, NWGetDiskCacheStats,
* NWGetPhysicalDiskStats, GetDriveMappingTable, and the NLM
* Get*Stats forms.
*
* MARS-NWE does not currently maintain NetWare FAT/cache,
* TTS, SFT mirror, physical-disk, or disk-channel tables, so
* do not synthesize fake counter blocks. Future ownership
* belongs to a servermgmt/statistics provider with real
* filesystem, volume, and transport/backend input.
*/
return(-1);
break;
case 0xdb : /* SDK 23/219 / wire 0xdb Get Connection's Open Files (old) */
case 0xdc : /* SDK 23/220 / wire 0xdc Get Connection Using A File (old) */
case 0xdd : /* SDK 23/221 / wire 0xdd Get Physical Record Locks By Connection And File (old) */
case 0xde : /* SDK 23/222 / wire 0xde Get Physical Record Locks By File (old) */
case 0xdf : /* SDK 23/223 / wire 0xdf Get Logical Records By Connection (old) */
case 0xe0 : /* SDK 23/224 / wire 0xe0 Get Logical Record Information (old) */
case 0xe1 : /* SDK 23/225 / wire 0xe1 Get Connection's Semaphores (old) */
case 0xe2 : /* SDK 23/226 / wire 0xe2 Get Semaphore Information (old) */
/*
* NetWare 2.x/3.x monitor scan block. These older
* selectors report per-connection open-file state, file-user
* state, physical-record locks, logical-record locks, and
* semaphores. The uploaded WebSDK material maps the same
* families through scan APIs such as NWScanOpenFilesByConn2,
* NWScanConnectionsUsingFile, NWScanPhysicalLocksByConnFile,
* NWScanPhysicalLocksByFile, NWScanLogicalLocksByConn,
* NWScanLogicalLocksByName, NWScanSemaphoresByConn, and
* NWScanSemaphoresByName.
*
* Keep disabled until the file-handle, byte-range-lock,
* logical-lock, and semaphore providers can expose real
* NetWare-compatible monitor records. Do not fake lists
* from only the connection table, and do not route this
* read-only data plane through nwserv.
*/
return(-1);
break;
case 0xe8 : /* SDK 23/232 / wire 0xe8 Get File Server Misc Information */
case 0xeb : /* SDK 23/235 / wire 0xeb Get Connection's Open Files */
/*
* NetWare 2.x/3.x server-management information calls.
* Keep disabled until their variable reply records are
* mapped to real MARS-NWE state.
*/
return(-1);
break;
case 0xfd : /* SDK 23/253 / wire 0xfd Send Console Broadcast */
/*
* Newer variant of SDK 23/209: StationList is long[] and
* SubFuncStrucLen = 3 + NumberOfStations * 4 + MessageLen.
*/
return(-1); /* nicht erkannt */
break;
case 0xfe : /* SDK 23/254 / wire 0xfe Clear Connection Number */
/*
* Newer variant of SDK 23/210: request carries a long
* ConnectionNumber instead of the old byte value.
*/
return(-1);
break;
#endif
default : completition = 0xfb; /* not known here */
break;
} /* switch */
} else if (func == 0x18) { /* End of Job */
/*
* Forwarded direct NCP 0x2222/24 End of Job.
*
* handle_fxx() does not consume a grouped subfunction header for this
* direct function: requestdata points at the empty post-FunctionCode
* payload. nwconn has already released task-local handles; nwbind only
* closes the matching print jobs and returns a normal empty reply.
*/
if (!(entry8_flags&0x200)) /* pcz: 14-Apr-00 */
nw_close_connection_jobs(act_connection, ncprequest->task); /* close print jobs */
} else if (func == 0x19) { /* logout */
/*
* Forwarded direct NCP 0x2222/25 Logout.
*
* handle_fxx() does not consume a grouped subfunction header for this
* direct function: requestdata points at the empty post-FunctionCode
* payload. nwconn has already released local queue/file/user state;
* nwbind closes any remaining print jobs, records logout state, clears
* bindery identity, and returns a normal empty reply.
*/
nw_close_connection_jobs(act_connection, -1); /* close all print jobs */
write_utmp(0, act_connection, act_c->pid_nwconn, &(act_c->client_adr), NULL);
act_c->object_id = 0; /* not LOGIN */
act_c->id_flags = 0; /* no flags */
} else if (0x20 == func) { /* Semaphore */
/*
* NCP 0x2222/32 Semaphore forwarded from nwconn.c.
* This group is parsed differently from 0x15/0x16/0x17:
* ufunc = requestdata[0]
* rdata = requestdata + 1
* handle_func_0x20() owns the concrete semaphore subfunction layouts.
*/
int result = handle_func_0x20(act_c, rdata, ufunc, responsedata);
if (result > -1) data_len = result;
else completition=(uint8)-result;
} else completition = 0xfb;
U16_TO_BE16(0x3333, ncpresponse->type);
ncpresponse->sequence = ncprequest->sequence;
ncpresponse->task = ncprequest->task;
ncpresponse->connection = ncprequest->connection;
ncpresponse->high_connection= ncprequest->high_connection;
ncpresponse->completition = completition;
if (act_c->message[0]) connect_status |= 0x40;
ncpresponse->connect_status = connect_status;
data_len+=sizeof(NCPRESPONSE);
U16_TO_BE16(act_c->send_to_sock, my_addr.sock);
send_ipx_data(ipx_out_fd, 17, data_len, (char*)ncpresponse,
&my_addr, NULL);
XDPRINTF((6, 0, "func=0x%x ufunc=0x%x compl:0x%x, written count = %d",
(int)func, (int)ufunc, (int) completition, data_len));
}
/* mst:25-Apr-00 */
static void send_request_to_nwconn(int connection, int function,
char *data, int data_len,
RESPONSE_FUNC func)
{
CONNECTION *c = &(connections[connection-1]);
if (c->active && c->send_to_sock > 0) {
IPX_DATA buf;
NCPREQUEST *req = (NCPREQUEST*)&buf;
U16_TO_BE16(0x2121, req->type);
delete_nwconn_request(c);
if (++c->last_used_sequence > 255)
c->last_used_sequence=1;
req->sequence = c->last_used_sequence;
req->task = 1;
req->connection = (connection & 0xff);
req->high_connection = ((connection >> 8) & 0xff);
req->function = function;
c->request_nwconn = (NWCONN_REQUEST*)xcmalloc(sizeof(NWCONN_REQUEST));
c->request_nwconn->sequence = req->sequence;
c->request_nwconn->function = function;
c->request_nwconn->sendtime = time(NULL);
c->request_nwconn->func = func;
if (data_len) {
memcpy(req+1, data, data_len);
c->request_nwconn->data = xmalloc(data_len);
memcpy(c->request_nwconn->data, data, data_len);
}
data_len+=sizeof(NCPREQUEST);
U16_TO_BE16(c->send_to_sock, my_addr.sock);
XDPRINTF((1, 0, "send_request_to_nwconn: connection=%d, data_len=%d",
connection, data_len));
send_ipx_data(ipx_out_fd, 17, data_len, (char*)req, &my_addr, NULL);
}
}
static void nwconn_callback(int connection,
char *data, int data_len,
int completition)
{
struct XDATA {
uint8 count_handles[4];
uint8 handles[4];
} *xdata = (struct XDATA*) data;
int open_files = GET_BE32(xdata->count_handles);
int handle0 = open_files < 1 ? 0 : GET_BE32(xdata->handles);
int handle1 = open_files < 2 ? 0 : GET_BE32(xdata->handles+4);
XDPRINTF((1, 0, "Got Response form con=%d, open_files=%d, handle0=%d, handle1=%d",
connection, open_files, handle0, handle1));
}
static void handle_usr2(void)
/* handles SIGUSR2, testfunction */
{
int k;
for (k=0; k < max_connections; k++) {
CONNECTION *c = &connections[k];
if (c->active) {
struct {
uint8 offset[4]; /* handle offset */
} data = { {0} };
send_request_to_nwconn(k+1, 0x1, (char*)&data, sizeof(data), nwconn_callback);
}
}
}
static void handle_bind_calls(uint8 *p)
{
int func = (int) *p;
p += 2;
switch (func) {
case 0x01 :
{ /* insert NET_ADDRESS */
NETOBJ obj;
obj.type = GET_BE16(p);
p += 2;
strmaxcpy(obj.name, p+1, *p);
p += (*p+1);
nw_new_obj_prop(0, obj.name, obj.type, O_FL_DYNA, 0x40,
"NET_ADDRESS", P_FL_DYNA|P_FL_ITEM, 0x40,
(char *)p, sizeof(ipxAddr_t), 1);
}
break;
case 0x02 :
{ /* delete complete Object */
NETOBJ obj;
obj.type = GET_BE16(p);
p += 2;
strmaxcpy(obj.name, p+1, *p);
nw_delete_obj(&obj); /* also deletes all properties */
}
break;
default : break;
}
}
static void reinit_nwbind(void)
{
int org_internal_act=internal_act;
get_ini_debug(NWBIND);
internal_act=1;
(void)nw_fill_standard(NULL, NULL);
internal_act=org_internal_act;
sync_dbm();
}
static int xread(IPX_DATA *ipxd, int *offs, uint8 *data, int size)
{
if (*offs == 0) memcpy(ipxd, &ipx_in_data, ud.udata.len);
if (size > -1 && *offs + size > ipxd->owndata.d.size) {
errorp(1, "xread:", "prog error: *offs=%d + size=%d > d.size=%d",
*offs, size, ipxd->owndata.d.size);
size = -1;
}
if (size > -1) {
memcpy(data, ((uint8 *)&(ipxd->owndata.d.function)) + *offs, size);
*offs+=size;
} else {
errorp(1, "xread:", "readerror");
}
return(size);
}
static void handle_ctrl()
/* reads packets from nwserv/ncpserv */
{
IPX_DATA ipxd;
int what;
int conn;
int offs=0;
int data_len = xread(&ipxd, &offs, (uint8*)&(what), sizeof(int));
if (data_len == sizeof(int)) {
XDPRINTF((2, 0, "GOT CTRL what=0x%x, len=%d",
what, ipxd.owndata.d.size));
(void)send_own_reply(ipx_out_fd, 0, ipxd.owndata.h.sequence, &from_addr);
switch (what) {
case 0x2222 : /* insert connection */
if (sizeof (int) ==
xread(&ipxd, &offs, (uint8*)&(conn), sizeof(int))) {
int size = 0;
if (sizeof (int) ==
xread(&ipxd, &offs, (uint8*)&(size), sizeof(int))
&& size == sizeof(ipxAddr_t)
+ sizeof(uint16) + sizeof(uint32) ) {
uint8 buf[100];
if (size == xread(&ipxd, &offs, buf, size))
open_clear_connection(conn, 1, buf);
}
}
break;
case 0x3333 : /* special 'bindery' calls */
if (sizeof (int) ==
xread(&ipxd, &offs, (uint8*)&(conn), sizeof(int))) {
uint8 *buff = (uint8*) xmalloc(conn+10);
XDPRINTF((2,0, "0x3333 len=%d", conn));
if (conn == xread(&ipxd, &offs, buff, conn))
handle_bind_calls(buff);
else
XDPRINTF((1, 1, "0x3333 protokoll error:len=%d", conn));
xfree(buff);
}
break;
case 0x5555 : /* clear connection */
if (sizeof (int) ==
xread(&ipxd, &offs, (uint8*)&(conn), sizeof(int)))
open_clear_connection(conn, 0, NULL);
break;
case 0xeeee:
reinit_nwbind();
break;
case 0xffff : /* server down */
data_len = xread(&ipxd, &offs, (char*)&conn, sizeof(int));
if (sizeof(int) == data_len && conn == what) {
XDPRINTF((1, 0, "handle_ctrl: nwbind got CTRL 0xffff, conn=0x%x", conn));
sent_down_message();
}
break;
default : break;
} /* switch */
} else {
errorp(1, "handle_ctrl", "wrong data len=%d", data_len);
}
}
static int got_sig=0;
static void sig_handler(int isig)
{
XDPRINTF((1, 0, "sig_handler: nwbind got signal=%d", isig));
got_sig=isig;
signal(isig, sig_handler);
}
static void set_sig(void)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; /* no SA_RESTART */
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGUSR2, &sa, NULL);
signal(SIGTERM, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
}
int main(int argc, char *argv[])
{
int sock_nwbind;
int i;
if (argc != 4) {
fprintf(stderr, "usage nwbind nwname address nwbindsock\n");
exit(1);
}
init_tools(NWBIND, 0);
strmaxcpy(my_nwname, argv[1], 47);
adr_to_ipx_addr(&my_addr, argv[2]);
sscanf(argv[3], "%x", &sock_nwbind);
internal_act = 1;
if (nw_init_dbm(my_nwname, &my_addr) <0) {
errorp(1, "nw_init_dbm", NULL);
exit(1);
}
internal_act = 0;
max_connections=get_ini_int(60); /* max_connections */
if (max_connections < 1)
max_connections=MAX_CONNECTIONS;
if (max_connections < 1)
max_connections=1;
connections=(CONNECTION*)xcmalloc(max_connections*sizeof(CONNECTION));
max_nw_vols=get_ini_int(61); /* max. volumes */
if (max_nw_vols < 1)
max_nw_vols = MAX_NW_VOLS;
#ifdef LINUX
set_emu_tli();
#endif
#if USE_PERMANENT_OUT_SOCKET
ipx_out_fd=open_ipx_socket(NULL, 0);
#endif
XDPRINTF((1, 0, "USE_PERMANENT_OUT_SOCKET %s",
(ipx_out_fd > -1) ? "enabled" : "disabled"));
ud.opt.len = sizeof(ipx_pack_typ);
ud.opt.maxlen = sizeof(ipx_pack_typ);
ud.opt.buf = (char*)&ipx_pack_typ; /* gets actual Typ */
ud.addr.len = sizeof(ipxAddr_t);
ud.addr.maxlen = sizeof(ipxAddr_t);
ud.addr.buf = (char*)&from_addr;
ud.udata.len = IPX_MAX_DATA;
ud.udata.maxlen = IPX_MAX_DATA;
ud.udata.buf = (char*)&ipx_in_data;
set_sig();
while (got_sig != SIGQUIT) {
act_c = (CONNECTION*)NULL;
if (t_rcvudata(ncp_fd, &ud, &rcv_flags) > -1){
time(&akttime);
XDPRINTF((10, 0, "NWBIND-LOOP from %s", visable_ipx_adr(&from_addr)));
act_ncpsequence = (int)ncprequest->sequence;
if ( ncprequest->type[0] == 0x22
&& ncprequest->type[1] == 0x22) {
act_connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
if (act_connection > 0 && act_connection <= max_connections) {
act_c = &(connections[act_connection-1]);
internal_act = 0;
if (act_c->active && IPXCMPNODE(from_addr.node, my_addr.node)
&& IPXCMPNET (from_addr.net, my_addr.net)) {
if (!ncprequest->function){ /* wdog reset */
nwserv_reset_wdog(act_connection);
XDPRINTF((3, 0, "send wdog reset"));
} else
handle_fxx(ud.udata.len, (int)ncprequest->function);
} else {
XDPRINTF((1, 0, "NWBIND-LOOP addr=%s of connection=%d is wrong",
visable_ipx_adr(&from_addr), act_connection));
}
} else {
XDPRINTF((1, 0, "NWBIND-LOOP connection=%d,adr=%s is wrong",
act_connection,
visable_ipx_adr(&from_addr)
));
}
} else if ( ncprequest->type[0] == 0x32
&& ncprequest->type[1] == 0x32 ) {
/* response from nwconn */
act_connection = (int)ncprequest->connection
| (((int)ncprequest->high_connection) << 8);
if (act_connection > 0 && act_connection <= max_connections) {
act_c = &(connections[act_connection-1]);
internal_act = 0;
if (act_c->active && act_c->request_nwconn
&& act_c->request_nwconn->sequence == ncprequest->sequence
&& IPXCMPNODE (from_addr.node, my_addr.node)
&& IPXCMPNET (from_addr.net, my_addr.net)
&& GET_BE16 (from_addr.sock) == SOCK_NCP ) {
XDPRINTF((1, 0, "GOT 0x3232k OK connection=%d", act_connection));
if (act_c->request_nwconn->func) {
(*act_c->request_nwconn->func)(
act_connection,
((char*)&ipx_in_data) + sizeof(NCPRESPONSE),
ud.udata.len - sizeof(NCPRESPONSE),
(int) ((NCPRESPONSE*)ipx_in_data)->completition);
}
/* now delete request */
delete_nwconn_request(act_c);
} else {
XDPRINTF((1, 0, "NWBIND-LOOP 0x3232 wrong connection %d, active=%d, seq=%d from %s",
act_connection, act_c->active,
(int) ncprequest->sequence, visable_ipx_adr(&from_addr) ));
}
} else {
XDPRINTF((1, 0, "NWBIND-LOOP 0x3232 wrong connection %d from %s",
act_connection, visable_ipx_adr(&from_addr) ));
}
} else if ( ncprequest->type[0] == 0xee
&& ncprequest->type[1] == 0xee
&& IPXCMPNODE(from_addr.node, my_addr.node)
&& IPXCMPNET (from_addr.net, my_addr.net)) {
/* comes from nwserv, i hope :) */
handle_ctrl();
} else {
XDPRINTF((1, 0, "NWBIND-LOOP got wrong type 0x%x func=0x%x from %s",
(int) GET_BE16(ncprequest->type),
(int) ncprequest->function, visable_ipx_adr(&from_addr) ));
}
}
if (got_sig == SIGHUP) {
/* here I update some Bindery stuff from nwserv.conf */
XDPRINTF((1, 0, "main: nwbind handling SIGHUP"));
reinit_nwbind();
got_sig = 0;
} else if (got_sig == SIGUSR2) {
XDPRINTF((1, 0, "main: nwbind handling SIGUSR2"));
handle_usr2(); /* mst:25-Apr-00 */
got_sig = 0;
} else if (got_sig == SIGQUIT) {
XDPRINTF((1, 0, "main: nwbind leaving main loop on SIGQUIT"));
}
}
/* perhaps some connections need clearing (utmp), hint from Ambrose Li */
i=max_connections+1;
while (--i)
open_clear_connection(i, 0, NULL);
if (ncp_fd > -1) {
t_unbind(ncp_fd);
t_close(ncp_fd);
if (ipx_out_fd > -1) {
t_unbind(ipx_out_fd);
t_close(ipx_out_fd);
}
}
internal_act=1;
nw_exit_dbm();
xfree(connections);
XDPRINTF((1,0, "LEAVE nwbind"));
return(0);
}