/* Copyright 1998 Acorn Computers Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * * RPC.C -- Remote procedure call routines for * interrogating servers * * 02-02-95 INH Original * Added Transact SWI interface * 25-07-96 Added GetUserHomeDir */ #define OMIT_UNUSED_FNS /* Standard includes */ #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <ctype.h> #include "kernel.h" /* Our includes */ #include "stdtypes.h" #include "buflib.h" #include "NetBIOS.h" #include "smb.h" #include "xlate.h" /* For string functions */ #include "omni.h" #include "lmvars.h" #include "rpc.h" /* Globals ---------------------------- */ char RPC_DebugMsg[100]; int RPC_ErrorCount=0; /* Debug routine */ static err_t debug_err ( err_t res, char *text, char *name ) { if ( res != OK ) { RPC_ErrorCount++; sprintf( RPC_DebugMsg, "%s %s: %s", text, name, Xlt_Error(res)->errmess ); } return res; } /* Parameter-assembly subroutines -------------------- */ static struct TransactParms TP; /* ---------- */ static void addword ( int value ) { BYTE *p = TP.parms_in + TP.parms_in_len; p[0] = (value & 0xFF); p[1] = (value >> 8 ); TP.parms_in_len+=2; } /* ---------- */ static void addlong ( int value ) { BYTE *p = TP.parms_in + TP.parms_in_len; p[0] = (value & 0xFF); p[1] = (value >> 8 ); p[2] = (value >> 16 ); p[3] = (value >> 24 ); TP.parms_in_len+=4; } /* ---------- */ static void addstring ( char *str ) { BYTE *p = TP.parms_in + TP.parms_in_len; int l = strlen(str)+1; memcpy ( p, str, l ); TP.parms_in_len += l; } /* ---------- */ static void StartParams ( int func_code, char *in_format, char *out_format, int ret_param_len ) { TP.parms_in = SMB_WorkBuf; TP.parms_in_len = 0; TP.data_in = NULL; TP.data_in_len = 0; TP.parms_out_buf = SMB_WorkBuf; TP.parms_out_maxlen = min(ret_param_len, SMBWORKBUF_SIZE); TP.data_out_buf = SMB_WorkBuf + TP.parms_out_maxlen; TP.data_out_maxlen = SMBWORKBUF_SIZE-TP.parms_out_maxlen; addword ( func_code ); addstring ( in_format ); addstring ( out_format ); } /* Parameter-return subroutines ====================== */ static int getword ( BYTE *p ) { return ( p[0] + (p[1] << 8)); } /* ----------------- */ #if 0 static int getlong ( BYTE *p ) { return ( p[0] + (p[1] << 8) + (p[2] << 16)+ (p[3] << 24)); } #endif /* ----------------- */ static BYTE *getpointer ( BYTE *p ) { int ptrval; ptrval = getword(p) + TP.data_out_len - TP.data_out_maxlen; if ( ptrval <= 0 || ptrval >= TP.data_out_len ) return NULL; return TP.data_out_buf + ptrval; } /* ============================================ */ static bool check_hidden ( char *name ) { while ( *name != 0 ) { if ( name[0] == '$' && name[1] == 0 ) return false; /* Name is hidden */ name++; } return true; } /* ---------------------------- */ static err_t RPC_EnumSharesOnConnection ( char drv, char *server ) { BYTE *p; int i, co; err_t res; /* Assemble parameters for RPC call */ StartParams( 0x0000, "WrLeh", "B13BWz", 8 ); /* NetShareEnum */ addword (0x0001); /* Detail level */ addword (TP.data_out_maxlen); /* Return buf size */ /* Make call */ res = SMB_Transact ( drv, "\\PIPE\\LANMAN", &TP ); if ( res != OK ) return res; if ( TP.parms_out_len < 8 ) return EDATALEN; /* Decode returned params */ p = TP.parms_out_buf; if ( getword(p) != 0 ) /* API return code; 0 = success */ return ERPCERROR; /* Otherwise, call it 'generic' error */ co = getword(p+2); /* Comment offset adjustment - ( why?? ) */ i = getword(p+4); /* Number of records returned */ if ( i*20 > TP.data_out_len ) /* Silly values! */ return EDATALEN; /* Process returned records */ p = TP.data_out_buf; while ( i-- > 0 ) { /* p is the start of the record. The first 13 bytes are a share name + null termination. If the share name ends in '$', it is hidden and should not be listed. [sbrodie: ... except if it's an IPC share, I've decided. Also we store the comments too for *lanman:lmls to display. Why is there a mystical word in the returned param block which is subtracted from the offset field? Dunno, but SAMBA does it and Windows 98 needs it.] */ int shrtype = getword(p+14); int commoffset = getword(p+16); char *comment = commoffset ? ((char *) TP.data_out_buf + commoffset - co) : 0; if ( shrtype == SHR_IPC || check_hidden( (char *)p) ) { if ( shrtype == SHR_DISK ) Omni_AddInfo ( OAI_DISK, server, (char *)p, comment ); else if ( shrtype == SHR_PRINTER ) Omni_AddInfo ( OAI_PRINTER, server, (char *)p, comment ); else if ( shrtype == SHR_IPC ) Omni_AddInfo ( OAI_IPC, server, (char *)p, comment ); else if ( shrtype == SHR_COMM ) Omni_AddInfo ( OAI_DEVICE, server, (char *)p, comment ); } p += 20; } return OK; } /* ---------------------------- */ static err_t RPC_EnumServersOnConnection ( char drv, char *domain ) { err_t res; BYTE *p; int co; int i; /* NetServerEnum2 */ StartParams ( 0x0068, "WrLehDz", "B16BBDz", 8 ); addword (0x0001); /* Detail level */ addword (TP.data_out_maxlen); /* Return buf size */ addlong (0xFFFFFFFF); /* Return all server types */ addstring ( domain ); /* Domain name */ /* Make call */ res = SMB_Transact ( drv, "\\PIPE\\LANMAN", &TP ); if ( res != OK ) return res; if ( TP.parms_out_len < 8 ) return EDATALEN; /* Decode returned params */ p = TP.parms_out_buf; if ( getword(p) != 0 ) /* API return code; 0 = success */ return ERPCERROR; co = getword(p+2); i = getword(p+4); /* Number of records returned */ if ( i*26 > TP.data_out_len ) /* Silly values! */ return EDATALEN; /* Process returned records */ p = TP.data_out_buf; while ( i-- > 0 ) { /* p is the start of the record. The first 16 bytes are a server name + null termination. */ int commoffset = getword(p+22); char *comment = commoffset ? ((char *) TP.data_out_buf + commoffset - co) : 0; Omni_AddInfo ( OAI_SERVER, (char *)p, comment, NULL ); p += 26; } return OK; } /* ---------------------------- */ err_t RPC_EnumerateShares ( char *server ) { char drv; /* Connection identifier */ err_t res; /* (i) Connect to IPC share */ res = SMB_CreateShare ( SHR_IPC, CREATE_NORMAL, server, "IPC$", NULL, NULL, &drv ); if ( res != OK ) return debug_err( res, "(EnumShares) could not log on to", server ); Omni_AddInfo ( OAI_SERVER, server, SMB_GetConnInfo(drv, GCI_SERVERINFO), NULL ); res = RPC_EnumSharesOnConnection ( drv, server ); SMB_DeleteShare ( drv ); return debug_err( res, "EnumShares call on", server ); } /* ---------------------------------------- */ static char *GetMasterBrowser (char *wg_name) { NETNAME MBname; struct FindName_res fnr; static char namebuf[16]; NB_FormatName ( ntMBROWSER, wg_name, &MBname ); /* Have a 1.5-sec timeout */ if ( NB_FindNames ( &MBname, ntSERVER, &fnr, 1, 150 ) == 0) return NULL; NB_DecodeName ( &(fnr.name), namebuf ); return namebuf; } /* ---------------------------------------- */ char *RPC_GetDomainController (char *domain_name) { NETNAME DCname; struct FindName_res fnr; static char namebuf[16]; NB_FormatName ( ntPRIMARYDC, domain_name, &DCname ); /* Have quick 1.5-sec timeout */ if ( NB_FindNames ( &DCname, ntSERVER, &fnr, 1, 150 ) == 0) return NULL; NB_DecodeName ( &(fnr.name), namebuf ); return namebuf; } /* ---------------------------- */ err_t RPC_EnumerateServers ( char *workgroup ) { char drv; /* Connection identifier */ err_t res; char *server; /* (i) Connect to IPC share */ server = GetMasterBrowser ( workgroup ); if ( server == NULL ) server = RPC_GetDomainController ( workgroup ); if ( server == NULL ) return debug_err( ECANTFINDNAME, "Can't find master browser or domain controller for", workgroup ); res = SMB_CreateShare ( SHR_IPC, CREATE_NORMAL, server, "IPC$", NULL, NULL, &drv ); if ( res != OK ) return debug_err( res, "(EnumServers) could not log on to", server ); res = RPC_EnumServersOnConnection ( drv, workgroup ); SMB_DeleteShare ( drv ); return debug_err( res, "EnumServers call on", server ); } /* ---------------------------- */ err_t RPC_LogonUser ( char *server, char *user, char *password, char **pHomeDir ) { err_t res; char drv; /* Trying to connect to the IPC share is as good a method of password validation as any */ res = SMB_CreateShare ( SHR_IPC, CREATE_NEW_USER, server, "IPC$", user, password, &drv ); if ( res != OK ) return debug_err(res, "LogonUser: connect to DC failed, user name", user ); /* NetUserGetInfo */ StartParams ( 0x0038, "zWrLh", "B21BzzzWDDzzDDWWzWzDWb21W", 6 ); addstring (user); addword (11); /* Detail level */ addword (TP.data_out_maxlen); /* Return buf size */ /* Make call */ res = SMB_Transact ( drv, "\\PIPE\\LANMAN", &TP ); if ( res == OK ) { if ( TP.parms_out_len < 6 ) res = EDATALEN; else { switch ( getword ( TP.parms_out_buf ) ) /* return code */ { case 0: *pHomeDir = (char *)getpointer( TP.data_out_buf+44 ); res = OK; break; case 5: case 65: /* Access denied */ res = ENOACCESS; break; case 2221: /* User not found */ res = EUSERUNKNOWN; break; case 2239: /* Account disabled */ res = EACCDISABLED; break; default: res = ERPCERROR; break; } } } SMB_DeleteShare ( drv ); return debug_err(res, "NetGetUserInfo() call failed, user name", user ); } /* ---------------------------- */ err_t RPC_NameOp ( int reason, char *name_in, char *buf_out ) { char *s; /* We assume buf_out can hold a 16-character name including last 0 */ switch ( reason ) { case NAMEOP_GETLOCAL: s = LM_Vars.machinename; break; case NAMEOP_GETWG: s = LM_Vars.workgroup; break; case NAMEOP_GETBROWSER: if ( name_in == NULL ) name_in = LM_Vars.workgroup; s = GetMasterBrowser ( name_in ); if ( s == NULL ) return ECANTFINDNAME; break; case NAMEOP_GETDC: if ( name_in == NULL ) name_in = LM_Vars.workgroup; s = RPC_GetDomainController ( name_in ); if ( s == NULL ) return ECANTFINDNAME; break; default: return EBADPARAM; } strcpy ( buf_out, s ); return OK; } /* ------------------------- */ err_t RPC_Transact ( char *servername, char *pipename, void *pvParmBlk ) { char drv; err_t res; struct TransactParms t; /* Connect to IPC share using default user ID/password */ res = SMB_CreateShare ( SHR_IPC, CREATE_NORMAL, servername, "IPC$", NULL, NULL, &drv ); if ( res != OK ) return res; memcpy(&t, pvParmBlk, sizeof_TransactParms_external); #ifdef LONGNAMES t.setup_in_len = 0; t.setup_out_maxlen = 0; #endif res = SMB_Transact ( drv, pipename, &t ); SMB_DeleteShare ( drv ); return res; } /* ------------------------- */ bool RPC_Init ( void ) { RPC_ErrorCount = 0; RPC_DebugMsg[0] = 0; return true; }