diff options
Diffstat (limited to 'source3')
59 files changed, 44490 insertions, 0 deletions
diff --git a/source3/change-log b/source3/change-log new file mode 100644 index 0000000000..e120ac6f02 --- /dev/null +++ b/source3/change-log @@ -0,0 +1,1872 @@ +Change Log for Samba + +Unless otherwise attributed, all changes were made by +Andrew.Tridgell@anu.edu.au + +NOTE: THIS LOG IS IN CHRONOLOGICAL ORDER + + +1.5.00 announced to mailing list + +1.5.01 1/12/93 + - configuration through makefile only + - fixed silly bug that made the client not accept dir's from + the server + - tested and updated include files for ultrix, aix and solaris + - several things fixed thanks to pierson@ketje.enet.dec.com + who provided invaluable help and advice. + +1.5.02 1/12/93 + - added username option to services file so connection + as non guest from lanmanager is possible + - made server abort when it can't read/write on a socket + - added logging to client + +1.5.03 2/12/93 + - printing now works + - fixed a minor bug to do with hidden and system attributes + +1.5.04 2/12/93 + - added reduce_name() call to fill in security hole. + - cleanup up debug stuff a little + +1.5.05 2/12/93 + - fixed bug in reduce_name that affects services with base paths + that have a soft link in them. + +1.5.06 3/12/93 + - used the reserved server field in the search status to hold the + directory pointer. This allows lots of directories to be open + at once by clients without stuffing things up. + - preserved all the client reserved bytes in the search status + in case they actually use them. Hopefully this will fix the annoying + empty directory dir bug. (it does) + +1.5.07 3/12/93 + - fixed silly bug that caused volume ids to appear twice + - fixed a wrote-too-few bug in smb_send() + +1.5.08 3/12/93 + - did the SMBsearch properly. It can now handle recursive searches. + In order to keep the required dir info I encode the dirptr and + the current dir offset (from telldir) into 5 bytes by using a table + on the last 7 bits of the first byte. The first bit is always on + as this byte must by != 0 + This is all put in the "server reserved" search field. + +1.5.09 5/12/93 + - added a prototype nameserver. It's broken but can at least interpret + incoming packets. + - minor fixes to the server and client + + +1.5.10 5/12/93 + - fixed silly unsigned/signed char bug that made dosshell noot see all files + - added nmbd to Makefile + +1.5.11 6/12/93 + - made the volume label appear as the service name, rather than "Remote" + - made the nmbd actually work (a little) for lanman for dos + +1.5.12 7/12/93 + - fixed broadcasting in the nameserver + - the smbd now correctly sets the pid and uid + - nmbd now seems to work enough to satisfy the MS client. + + +1.5.13 7/12/93 + - fixed a silly bug that truncated filenames + - added -B option to nameserver to specify bcast address + - added -R option to nameserver to prevent name registering + - fixed minor read() bug. Does this fix the "cmp" bug? + +1.5.14 8/12/93 + - fixed a bug in send_login() in the client. Thanks to + tim.hudson@gslmail.mincom.oz.au for pointing this out. + - changed name_mangle() to pad to minimum of 32 bytes with spaces + - changed the returned buffer size in reply_connect() to not + count the 4 byte length field. This fixes the "can execute" bug + and the "comp" bug + - once again re-wrote the directory pointer handling code. + now "tree" works correctly + +1.5.15 9/12/93 + - fixed name mangle bug introduced in 1.5.14 which stopped + nameserver from working + +1.5.16 9/12/93 + - arrgh. another silly bug in name_mangle() causes the client to die. + + +1.5.17 13/12/93 + - some cosmetic cleanups to the code + - changed make_connection not to lower case the password (thanks + to bryan@alex.com) + - fixed accept() bug not initialising in_addrlen (thanks to + bogstad@cs.jhu.edu) + - fixed cd bug in client.c (thanks to joergs@toppoint.de) + - lots of fixes to the nameserver to read_socket and + associated routines. It should now correctly reply to the originating + address and use the correct broadcast. + (thanks to troyer@saifr00.ateng.az.honeywell.com) + - SVR4 patches from mark@scot1.ucsalf.ac.uk + - changed the default BUFFER_SIZE to 0xFFFF + +1.5.18 15/12/93 + - minor fix to reply_printqueue() to zero data buffer array. + - added print command to client. + - fixed minor bug in cmd_put() in client where a handle could + be closed without being previously opened. + - minor cleanups to the client + - minor solaris fixes from lonnie@itg.ti.com + - SYSV, shadow password and dfree() fixes from mark@scot1.ucsalf.ac.uk + - fixed reply_delete() to not delete read-only files + - fixed infinite loop in reply_delete on "del ." + Thanks to mark@scot1.ucsalf.ac.uk for pointing this out. + - posix mode definitions and changes from mark@scot1.ucsalf.ac.uk + + +1.5.19 18/12/93 + - another very minor fix to dfree(). + - minor change to SVR4 makefile entry from rossw@march.co.uk + - changed reply_open not to open directories, this fixes the + "copy .." bug pointed out by mark@scot1.ucsalf.ac.uk + - changed dos_mode() so it doesn't return hidden and system info + on directories. + - changed get_dir_entry() not to descend into proc/self under linux + control this with the DONT_DESCEND define in includes.h + - changed smb_setlen() to add in the SMB id. (thanks + to troyer@saifr00.ateng.az.honeywell.com) + - fixed minor bug in reply_dir() so it won't return a ACCESS_DENIED + when searching a directory that is unreadable + - removed second stat() from get_dir_entry() (speed up) + - made null searches close the dirptr (fixes big filesystem problem) + - fixed clean_name for cd .. (from magnus@axiom.se) + + +1.5.20 28/12/93 + - added debug statement in case of SMBcreate with volid set (leefi@microsoft.com) + - fixed a bug in dptr_close() so it sets the next_key to a better + value, this fixes a annoying dir bug + - LOTS of changes from jeremy@netcom.com (Jeremy Allison). This + makes it possible to at least connect to a NT server with the client + and also fixes up much of the socket/process code. This also includes + stuff for compiling on a sun386 + - got the client working with the Syntax server (a commercial + smb-based server). This required a few minor changes so the xmit + sizes were negotiated properly. + - added support for OSF1, tested on a DEC3000/400 alpha. + - fixed the ifconf support under ultrix + +1.5.21 31/12/93 + - minor cosmetic change to reduce_name() + - changes for HPUX from ppk@atk.tpo.fi (Pasi Kaara) + - minor fix to nameserver + - revamped configuration file format. It now takes a Windows-style + (.INI style) configuration file. See the file services for + full details of the format. New files: loadparm.c, loadparm.h, + params.c, params.h, testparm.c. Several changes to smb.h, local.h, + server.c, Makefile. The services structure is no longer visible + to the rest of the system. (Karl Auer) + - added ability to specify a print command on a per service basis + and globally via the configuration file. Also allows guest account + to be specified in the configuration file. Made appropriate changes + to server.c so that these data items are obtained from the config + module rather than from hardcoded strings (though the hardcoded + strings are still the source of the defaults). (Karl Auer) + - renamed old-style configuration file to services.old (Karl Auer) + - changed README to reflect new configuration details. (Karl Auer) + - removed an item from the bugs wishlist (now supplied!) (Karl Auer) + - protected smb.h against multiple compilation. (Karl Auer) + - protected local.h against multiple compilation. (Karl Auer) + - made config stuff do dynamic allocation + - added "homes" capability + - added create_mask to each service in config + +1.5.22 3/1/94 + - added "root dir" option for extra security + - added -n option to client (useful for OS/2) + - changed operation of -n to nameserver to be more useful + - patches from Jeremy Allison (jeremy@netcom.com) + fixing bug in set_message(), fixing up wait3() for SYSV, + making cd check the path in the client, allowing fetching to stdin + in client, and enhancing prompt in client to include directory. + - made the -D become_daemon() actually detach from the tty. This + may need tuning for different flavors of unix. + - added "dont descend" option to each service to prevent infinite + loops on recursive filesystems. + - updated README to add "running as a daemon" and a simple + smb.conf file. + - HP/UX fixes from ppk@atk.tpo.fi + - made lock calls only if opened with write enabled, as pointed out + by gadams@ddrive.demon.co.uk + +1.5.23 4/1/94 + - minor fix to logging of data in receive_smb(). It used to + miss the last 4 bytes of packets. + - added the pid,uid and mid fields to the negotiation phase of + the client. + - made client able to print from stdin + - added password on command line for client + - created a sample printcap input filter "smbprint" + - several fixes to client to work with OS/2 + - added mput, mget, prompt and lcd to client + +1.5.24 5/1/94 + - a resend of 1.5.23 as I managed to not include the new + prompt, mput and mget code. + +1.5.25 7/1/94 + - change -B on nameserver so it can override the broadcast address + - minor changes to printing in client so OS/2 server can handle it. + - fixed reply_access() where OK was not being initialised + - added "max xmit" to global parameters. + - changed create to open with O_RDWR instead of O_WRONLY + - added printmode command to client + - made help return extra help on a specified command in client + - fixed return code in chkpath + - added "recurse" and "lowercase" options to client + - fixed some error codes from server + - added -I option to client + - fix for become_daemon() for HPUX from ppk@atk.tpo.fi + - added "hosts allow" and "hosts deny" to server + - added keepalives to server + - added "access" feature to testparam + - NetBSD patches from sreiz@aie.nl + +1.5.26 8/1/94 + - changed semantics of hosts access code to do more sensible defaults + when either of "hosts allow" or "hosts deny" is blank + - added the SO_KEEPALIVE option to configurations of sockets in the + server + - made some of the SVAL fns into macros to keep fussy compilers from + complaining + - fixed several null pointer bugs in check_access(). These bugs + made 1.5.25 unuseable for many people. + - fixed null pointer reference of lp_dontdescend() + - reload services file after each new connection. + +1.5.27 11/1/94 + - fixed opening mode for reply_open() in server + - patches from Jeremy Allison (jeremy@netcom.com) to support the + "core+" protocol. The patches also inclued some other features, such + as a new read_with_timeout() call (used by SMBreadbraw), and auto + detection of the need to create a socket. + - changed the default KEEPALIVE value to 0, as it caused + problems with Lanmanager. + - added tar capability to client when getting files + - altered unix_mode() to return x bits for directories + - fixed bug in trim_string() + +1.5.28 12/1/94 + - cleaned up the debug levels a little so debug level 1 is a practical + level for general use + - fixed a bug in add_a_service() where a freed pointer was referenced. Thanks + to bryan@alex.com for finding the bug. + - fixed bug in time structure handling in server and client. Thanks to + bryan@alex.com for pointing out the bug. + + +1.5.29 15/1/94 + - fixed a silly bug in reply_open(). Thanks to + jeremy@netcom.com for pointing this out. + - fixed debug levels in client to be more sensible + - added raw read to client + - added -B option to client + - fixed several bugs in the client, mostly to do with the tar option + - added -E option to client + +1.5.30 16/1/94 + - added lots of prototypes so compilers don't complain + - fixed minor bug in reply_rename() (thanks to ppk@atk.tpo.fi) + - added more support for LANMAN1.0 protocol. + - added SESSION SETUP AND X call + - added READ AND X call + - added TREE CONNECT AND X call + - added support for setbuffer for HPUX (thanks to ppk@atk.tpo.fi) + +1.5.31 29/1/94 + - added support for user level security in smbclient eg: + smbclient "\\SERVER\SHARE" -U USERNAME%PASSWORD + - added error message decode as per SMB File Sharing + protocol extensions. (thanks to merik@blackadder.dsh.oz.au) + - added selection masks to smbclient that recurse down directory + tree. eg: mget *.* with recurse and mask *.c on will retrieve all + *.c files in the tree. + - patches for FreeBSD from kuku@acds.physik.rwth-aachen.de + - changed reduce_name() to trim ./ from front of strings and / from + back + - fixed a nasty bug in trim_string(). + - numerous small changes to lots of stuff that I didn't + document while I was doing them. Sorry :-( + - slightly updated sockspy + + - The following was done by Karl Auer (Karl.Auer@anu.edu.au) + - added processing in configuration file of a [printers] section. Allows + connection to any printer specified in /etc/printcap (or the file + specified in the global parameter 'printcap name'). + - added full processing of 'available' flag to configuration file. A + service can now be 'turned off' by specifying 'available = no'. Of + dubious utility. + - added 'printcap =' parameter to [global] section in the configuration + file. This allows the normal /etc/printcap to be bypassed when + checking printer names for dynamic printer connections via [printers]. + - added 'printer name =' parameters to both the [global] section and + services sections of the configuration file. This allows the printer + name only to be set, without having to specify an entire print + command. + - added some synonyms: 'writable' and 'write ok' have the opposite sense + to 'read only'. 'public' may be used instead of 'guest ok'. 'printer' + may be used instead of 'printer name'. 'printable' is the same as + 'print ok'. 'root' may be used instead of 'root dir' or 'root + directory'. + - added lots more detail to the sample configuration file to take + account of the above. + - many minor fixes to internal documentation in the configuration + sources. + - also - Man pages! + + +1.5.32 3/2/94 + - addition of smbd, smbclient and testparm man pages + from Karl Auer + - zombie process fix from lendecke@namu01.gwdg.de + - added capability to nmbd to serve names available + via gethostbyname(). + +1.5.33 3/2/94 + - fixed up getting of netmask so it works on more unix variants + - added -N option to nmbd + - changed GMT diff calculation. need to check it's right for + lots of OSes + - fixed a bug in read_and_X() and chain_reply() chaining now + seems to work correctly + +1.5.34 4/2/94 + - fixed bug in client that meant it couldn't get/put files from WfWg + - fixed a bug in the server that caused lpr to return -1 under sunos + - fixed a few errors in the hosts allow section of the + smb.conf.5 manual page and added examples + +1.5.35 6/2/1994 + - minor bugfix in reduce_name(). + - changed width of "size" in client during a dir + - patches for NEXT (among other things) from lendecke@namu01.gwdg.de + - added -a switch to server, and made default action to append + to log file + - added deadtime options to [global] section for timing out + dead connections to the smbd. + - HPUX changes from Pasi.Kaara@atk.tpo.fi + - made use of unsigned char more consistent + - changed the way of getting the default username and host in the + client + - made LANMAN1 default to on in the client, off in server. + Use -DLANMAN1=1 to make it on in both. + - lots of casts and cleanups for various operating systems + - changes to the Makefile from Karl to auto-instal the man pages + - added a short history of the project to the distribution + +1.5.36 15/2/94 + - fixed minor bug in Debug() (thanks to Pasi.Kaara@atk.tpo.fi) + - fixed bug in server.c so -a wasn't accepted. + - minor fixes to the client + - added hosts file to name server (-H option) + - added -G option for groups to nameserver + - cleanups and additions from Jeremy Allison, taking us + closer to LANMAN1.0. In particular the locking code was cleaned up + considerably. + +1.5.37 16/2/94 + - fixed bug introduced in 1.5.36 which disabled SMBcreate + +1.5.38 18/2/94 + - fixed get_broadcast() for ultrix (fix from iversen@dsfys1.fi.uib.no) + - added automatic group registration + - fixed bug in registration code + - made nmbd work better with WfWg, and probably others + - updated the man pages to include the new nmbd options. + - minor updates to the README + - fixed double log_out() in send_packet(). + - fixed bug in smbclient so that "dir" didn't work correctly + with pathworks + - possibly fixed bug in server that led to "abort retry ignore" from + pathworks client when doing a "dir". + - changed behaviour of smbclient login slightly, to try a + blank password in SMBtcon if the right password fails, and a + session setup has succeeded. Some clients seem to use a blank + one if a session setup has succeeded. + - ISC patches from imb@asstdc.scgt.oz.au + - the client now tries to do name registration using a unicast. + Let me know if this helps anyone. + - tried to add a "contributed" line to each OS in the Makefile. + +1.5.39 18/2/94 + - fixed silly C code that only worked with some compilers + - fixed another silly bug in nameserv.c that caused it to seg fault + +1.5.40 21/2/94 + - removed the from (IP) message so people don't worry about 0.0.0.0, + it's redundant anyway. + - changed the client so the crypt key isn't printed + - changed the structure of switch_message() to use a list of functions. + This improves the debug info. + - made SMBopen ignore supplied attribute as per X/Open spec + - made SMBopen fail if file doesn't exist in all cases. Let me know + if this breaks something. It is implied in the X/Open spec. This + fixes the pkzip bug. + - added dptr_demote() to replace dptr_close() to try and fix + pathworks dir bug. This has the potential disadvantage of + leaving lots of open file descriptors. + - changed mask_match to disallow two .s in a name + +1.5.41 2/3/94 + - added "dfree command" global option to smbd to support an + external "disk free" executable (typically a script). This gets + around the problem of getting disk free info reliably on lots + of systems. + - added ffirst and fclose to client + - simple SYSVR4 patch from mark@scot1.ucsalf.ac.uk + - added better uid/gid reporting for debugging purposes + - several changes to the logon procedure for the client, so hopefully + it will connect correctly to a wider range of servers. + - server should no longer crash if it can't open the debug + file (thanks to MGK@newton.npl.co.uk) + - added the THANKS file. + +1.5.42 6/3/94 + - lots of changes from Jeremy Allison, implementing more of + the LANMAN1.0 protocol, and fixing a few bugs. + - fixed delete bug, so hopefully wildcards are correct now + - pcap changes from Martin Kiff so non-aliased printers in + /etc/printcap are recognised + - wrote announce file ready for 1.6 + - re-wrote browse code in client (still doesn't work) + - updates to man-pages from Karl Auer + - made raw packet dumps mode 0600 and only if -dA is given + - changed socket code to use utility functions in util.c + +1.6.00 17/3/94 + - made server always return to original directory (rather than /) + - fixed bug in params.c that caused a seg fault if no parms in a + section + - minor clean ups for clean compile under solaris + - solaris fix for running from inetd from Karl Auer + - fixes for dfree() under solaris + - minor changes that might help BSDI + - changes to the Makefile, manual-pages and sample config file from + Karl Auer + - fixed dfree for Ultrix + +1.6.01 19/3/94 + - fixed setatr bug that allowed directories to be unusable + +1.6.02 27/3/94 + - added timestamps to connection message in log + - added idle timeout of 10 minutes to name server + - made HAVE_SYSCONF==0 the default in includes.h + - made the client not register by default + - ISC patches from imb@asstdc.scgt.oz.au + - GetWd() cache code from Martin Kiff + - rewrote the locking code in terms of fcntl() calls. + - fixed "can't delete directory" bug + - added code to close old dirptrs for duplicate searches + - removed exchange_uids() and the access() call and replaced them. + +1.6.03 28/3/94 + - tried to clean up the time handling a little (local vs gmt time) + - added debug level global to server config + - added protocol level global to server config + - added SMBecho command to server + - included Karl Auers SMBGuide in the distribution. + +1.6.04 31/3/94 + - fixed time zeroing bug in smb_close and smb_setatr + - re-wrote the username/password handling to be more flexible + - added "guest only" service setting to smb.conf + - updated man pages for new username/password handling + - fixed parse bug in reply_tconX + - improved error return code from tcon + - several changes to fix printing from WfWg + +1.6.05 2/4/94 + - changed the name of the whole package to Samba + - removed SMBexit call from client to stop exiting error message + - added interpret_addr() call to replace inet_addr() so + a hostname can be used whenever a IP is required + +1.6.06 8/4/94 + - added random tid choice to reduce problem of clients not + detecting a server disconnection. + - made client not report spurious time from CORE or COREPLUS server. + - minor HPUX fix from gunjkoa@dep.sa.gov.au + - turned off GETWD_CACHE until we track down a minor bug in it + +1.6.07: 10/4/94 + - added helpful error messages to connection failure in client. + - fixed problem with mput in client + - changed server to allow guest-only sesssetup messages with any + password. Control this with GUEST_SESSION_SETUP in local.h. + - minor change to session setup handling in make_connection() + - added check for right number of \s in the client. + - made the server not exit on last close if the deadtime is != 0 + - added malloc and realloc wrappers. enable them with -DWRAP_MALLOC=1 + - if smbd is started with a debug level of 10 or greater it creates + a log file ending in the process number + +1.6.08: 18/4/94 + - updated the THANKS file + - changes from marcel@fanout.et.tudelft.nl (Marcel Mol) for AMPM + times and error report on connect(). + - made the get_myname() routine discard any part after the first '.' + - added a wrapper for free from Martin Kiff + - added simpleminded code to handle trapdoor uid systems (untested) + - added Martin Kiffs "paranoid" getwd code. + - added default MAXPATHLEN if undefined of 1024 + - made get_broadcast() continue to get netmask if it can't get + broadcast (suggestion from Hannu Martikk) + - replaced fchmod() calls with chmod() to satisfy some unixes + + + +1.6.09: 4/5/94 + - changed perror() calls to strerror() in server.c + - fix for dfree on OSF1 from + Maximilian Errath (errath@balu.kfunigraz.ac.at) + - fixed server time reporting for protocol >= LANMAN1 + - fixed TimeDiff() for machines without TIMEZONE or TIMELOCAL + (thanks to Vesa S{rkel{ <vesku@rankki.kcl.fi>) + - added SYSV defs to AIX and HPUX to fix "memory" problem + (actually a signal problem). + - added version to client banner in log file + - Ultrix patches from Vesa S{rkel{ <vesku@rankki.kcl.fi> + - added ! command to client for executing shell commands + - fixed ERRnofids bug in server + - fixed name_equal bug + (thanks to cjkiick@flinx.b11.ingr.com (Chris Kiick)) + - wrapped gethostbyname() with Get_Hostbyname() to prevent + case sensitive problems on name lookups + - limit printer tmp filename to 14 chars + (from Paul Thomas Mahoney <ptm@xact1.xact.com>) + - added ability to understand 64 bit file times + (thanks to davidb@ndl.co.uk (David Boreham)) + - added Gwt_Pwnam() wrapper to cover server case-sensitivity + problems (suggestion from J.M.OConnor@massey.ac.nz (John O'Connor)) + - changed the setuid() calls to try and work for more systems + without breaking the ones it currently works for + - added version number to usage() + (suggestion from peter@prospect.anprod.csiro.au) + - added "security=" option for share or user level security + - allowed multiple usernames in "user=" field + - changed display method for recursive dorectory listings + - switched client to use long filenames where supported + - added speed reporting to client transfers + - several NT fixes to server from jra@vantive.com (Jeremy Allison) + - ISC fixes from ptm@xact.demon.co.uk (Paul Mahoney) + - fix to README from grif@cs.ucr.edu (Michael A. Griffith) + - default netmask and broadcast from Ian A Young <iay@threel.co.uk> + - changed default of is_locked() on fcntl() error. + - fixed bug in read_with_timeout() that could cause a runaway + smbd process. + - fixed findnext bug for long filenames in client + - changed default protocol level to LANMAN1 + - change default reported security level to SHARE. + - changed password_ok() so that if pwdauth() fails it tries + with standard crypt. + - added "translate" command to the client to do CR/LF translation + for printing, and add a form feed at the end. + (thanks to mh2620@sarek.sbc.com (Mark A. Horstman ) ) + - added "locking=yes/no" toggle for each service + - SCO unix patches from Heinz Mauelshagen (mauelsha@ez.da.telekom.de) + +1.6.10: 7/5/94 + - fixed important bug in readbraw/writebraw + - added -A option to client + - fixed delete bug on long filenames (untested). Thanks to + Stefan Wessels <SWESSELS@dos-lan.cs.up.ac.za> + - neatened up the byte swapping code + +1.6.11: 3/6/94 + - fixed bug in client in receive_trans2_response() that caused + some strange behaviour with LANMAN2. + - fixed some offset/alignment problems with lockingX (thanks to + Jeremy Allison) + - allow locking on O_RDONLY files. Thanks to Martin N Dey <mnd@netmgrs.co.uk> + - fixed del bug in client thanks to paulzn@olivetti.nl (Paul van der Zwan) + - fixed multiple user= bug thanks to MDGrosen@spectron.COM (Mark Grosen) + - added translate ability for all files. Thanks to mh2620@sarek.sbc.com (Mark A. Horstman ) + - mask out negative lock offsets. Thanks to bgm@atml.co.uk (Barry G Merrick) + - more attempts to get the structure alignment better for some machines + - cleaned up the machine dependencies a little + - ISC fixes from Paul Thomas Mahoney <ptm@xact1.xact.com> + - enabled printing with a SMBclose and SMBwrite for NT + thanks to jkf@frisky.Franz.COM (Sean Foderaro) + - SGI changes from Michael Chua <lpc@solomon.technet.sg> + - CLIX patches from cjkiick@ingr.com + - NEXT2 and NEXT3_0 patches from Brad Greer (brad@cac.washington.edu) + - BSDI changes from tomh@metrics.com (Tom Haapanen) + - SCO patches from John Owens (john@micros.com) + - fix psz bug in pcap.c (thanks to Karl Auer) + - added widelinks option (global and per service). Suggestion from + Karl Auer. Defaults to True. + - made locking able to be global or local (default is give by global) + - added check_name() to dir listings + - added "packet size" option to globals. default to 32767. This + "fixes" a WfWg bug (thanks to Karl Auer) + - fixes for getattrE and setattrE and minor fix in util.c from Jeremy Allison. + - Karl updated the man pages o be current + - disabled writebraw and readbraw until a possible bug can be investigated further + +1.7.00: 14/7/94 + - added session_users list, to overcome problem of missing usernames in SMBTconX. + - added term support to the client + - added "default service" + - fork for print so user is not root + - added name mangling to 8.3 (rudimentary) + - fixed bug in in_group() + - changed to use gid in place of egid + - fixed client connection to OS/2 (1.3 + lanman2.2) and long filenames + - added patches from mcochran@wellfeet.com (Marc Cochran) + these implement scope ids and fix some udp bugs. It means + the -L option to nmbd now works. + - made nmbd respond to incoming port rather than only 137 + - made wide links refuse .. components + - fixed "dir foo." bug to stop it showing "foo.???" + - improved name mangling (added stack) + - added valid FNUM check to most calls + - fixed important do_put bug in the client + - added magic scripts to the server + - re-enabled getwd_cache code + - added optional agressive password checking + - removed dptr_closepath from SMBsearch to try and stop "dos for loop" + bug + - DGUX patches from ross@augie.insci.com (ross andrus) + - updated the README and THANKS file. + - added node status request to -L option of nmbd + - stripped trailing spaces in mask_match() (thanks to mike hench hench@cae.uwm.edu) + - added COREPLUS style print queue reporting and "lpq command" + in globals. + - cleaned up date handling and fixed byte order dependancy on dates + in SMBgetattrE. + - cleaned up the password handling and added "password level" with + the possability of checking all case combinations up to N upper + case chars. + - changed to use recvfrom only on udp ports (fixed read raw!) + - added TCB password support for SCO (thanks to lance@fox.com) + - updated README, THANKS and announce files. + - fixed timezone reporting to be signed (thanks to noses@oink.rhein.de) + - disabled max packet as it could cause problems with WfWg (no longer + needed now readraw is "fixed") + - changed from creat() to open() in mktemp and mknew. + - changed umask handling + - sped up nmbd by making it cache names + - changed idle timeout on nmbd to 2 mins + - Netbsd changes from noses@oink.rhein.de + - released alpha2 + - added name timeout to nmbd + - changed bind port retry in nmbd + - added Limitations sections to README + - fixed two . in is_83() + - fixed compilations warnings in util.c (thanks to njw@cpsg.com.au) + - made [homes] honour multiple user list + - fixed mask match bug introduced in alpha1 + - added "mangled stack" option for stack size + - added mangled stack promotion + - released alpha3 + - netbsd-1.0 fix for statfs(). + - added null_string to util.c to reduce memory usage + - changed the way directory structures are put together + - added smbrun for system() requests + - changed maxmux to 0 in hope of avoiding mpx commands problem + - fixed zero response length for session keepalives + - removed called name from session users list + - added F_RDLCK support to try and handle locks on readonly files + - made directory creation honour the lowercase flag in client (thanks + to charlie@edina.demon.co.uk) + - made checksum for mangling independant of extension if extension is + lowercase + - added ability to rename files with different extension, preserving + root name + - released alpha4 + - better command line error checking in client + - changed all debug statements to new format + - fixed delete error code reporting + - released alpha5 + - added mangled name support to wildcard delete in server + - fixed mask bug in SMBsearch + - cleaned up prototypes + - released alpha6 + - fixed important bug in session_setup which made WfWg freeze + (maxmux was 0 - this bug was introduced in alpha4) + - released alpha7 + - two printing bug fixes thanks to bgm@atml.co.uk (Barry G Merrick) + - uid fix to smbrun (thanks to larry@witch.mitra.com) + - man page updates from Karl Auer + - FAQ file from Karl Auer + - released alpha8 + - fixed read-only flag in dos_mode() for non writeable services + - fixed error code reporting in open() and openX(). + - minor secureware fix from (thanks to lance@fox.com) + - released alpha9 + - casting cleanups for memcpy(). + - cleaned up error code names to be more consistant + +1.7.01: 17/7/94 + - minor man page fix from baeder@cadence.com (Scott Baeder) + - changed usage() error message in client + - made nmbd not exit if can't register own name + - made nmbd only register if running as a daemon + - fixed stdout problem in smbrun by closing stdin/stdout/stderr + - minor fix to lmhosts parsing + + +1.7.02: 20/7/94 + - made nmbd not call get_broadcast if both -B and -N are used (thanks + to Chris Woodrow <Chris.Woodrow@actrix.gen.nz>) + - disabled GETWD_CACHE again + - fixed INCLUDES list in Makefile to add version.h (thanks to + jimw@PE-Nelson.COM (Jim Watt)) + - made checkname do a become user if it hasn't already done so. + - added consistancy check to become_user(). + - removed mask extension expansion from SMBsearch + - small change to chkpth + - fix to snum select for lpq status (thanks to Rafi Sadowsky + rafi@tavor.openu.ac.il) + - changed daemon to is_daemon for NetBSD (thanks to noses@oink.rhein.de) + - removed STAFS3 stuff for NETBSD_1_0 + + +1.7.03: 29/7/94 + - updated docs for new distribution structure + - made getatr return 0 size for directories (thanks to Bernd Esser + esser@pib1.physik.uni-bonn.de) + - added valid dos filename checks from Stefan Wessels + (swessels@cs.up.ac.za) + - added trimming of . in hostnames to -S mode of nmbd + - removed become_user() and OPEN_CNUM calls. Now make them + in switch_message instead which simplifies a lot of code. + - added GETFNUM macro to make chain_fnum more consistant and + reliable. + - added flags to protocol structures to simplify CAN_WRITE and AS_USER + checking + - added getwd cache boolean option to globals + - added fclose() to lpq status routine thanks to + dgb900@durras.anu.edu.au (David Baldwin) + - added "only user" option, to limit connection usernames to those + in the user= line + - changed to badpath from badfile in chkpath despite specs (following + what WfWg does). This fixes "file not found" error in copy command. + Thanks to rwa@aber.ac.uk for pointing out the bug + - changes for apollo from Stephen C. Steel <steve@qv3donald.LeidenUniv.nl> + - more changes for Apollo from jmi@csd.cri.dk (John Mills) + - released alpha release + - added FTRUNCATE_CAN_EXTEND=0 as default to fix problem with word6. + Possibly not needed on many OSes? Thanks to Charlie Hussey + charlie@edina.demon.co.uk + - started adding max connections code + - much improved group handling contributed by + Ian Heath (ih@ecs.soton.ac.uk) + +1.7.04: 29/7/94 + - fixed one line bug in SMBopenX that got error code wrong. + +1.7.05: 2/8/94 + - added UNIXERROR() macro to get error code from unix errno. + - fixed lpq status for MSTCPB3 + - added @ option for user= line to lookup groups in group file + - added become_user optimisation and process timeout (thanks to + Jeanette Pauline Middelink (middelin@calvin.iaf.nl) + - added malloc optimisation in readbraw + - released alpha + - patches for OSF1 enhanced security from Udo Linauer <ul@eacpc4.tuwien.ac.at> + - made level 2 a more useful debug level (less guff) + - added "max connections" and "lock dir" options to allow + limiting of the number of connections to a service at one time. + - released alpha2 + - updated man pages + - released alpha3 + - added read prediction code for better read performance + - released alpha4 + - minor tuning to receive_smb() + - changed the order of mangled stack checking + - bug fix in read_predict(). + - released alpha5 + - minor search optimisation + - fixed keep alive bug in writebraw and in readbraw in the client + - released alpha6 + - disabled writeraw by default pending a bug fix + - added profiling code (off by default) + - minor delete tuning + + +1.7.06: 4/8/94 + - OSF1 crypt fix thanks to Udo Linauer <ul@eacpc4.tuwien.ac.at> + - ifdef'd EDQUOT in case you don't have it (thanks to Paul Blackman <ictinus@Lake.canberra.edu.au>) + - tidied up UNIXERROR stuff to work on more systems. + - made Makefile more sophisticated and added "make revert" + +1.7.07: 4/8/94 + - fixed one line fatal bug in receive_smb. Thanks to bruce@pixar.com + +1.7.08: 2/9/94 + - initgroups call for SCO from lance@fox.com + - code cleanups from cap@isac.hces.com (Simon Casady) + - use full pathname in print command construction + - ISC includes fix from Martin Tomes <mt00@ecl.etherm.co.uk> + - added GID_TYPE define to cope with ultrix. Thanks to + brad@cac.washington.edu + - added umask call to main in server + - fixed several minor problems with the max connections + code. Thanks to lehmann@klizix.mpi-stuttgart.mpg.de (Arno Lehmann). + - fixed filetime in writeclose. Thanks to Andreas Bahrdt + <100321.2431@compuserve.com> + - df fix for large disks from Andreas Bahrdt + - getpwanam support from horn@mickey.jsc.nasa.gov + - clean name change from Bernd Esser + <be@syli30.physik.uni-bonn.de> + - released alpha1 + - more locking changes to fix Excel problem + - released alpha3 + - another minor locking change + - smarter masking in the locking code. Excel now apparently works. + - minor FAQ updates + - changed max connections refusal error to access denied. + - added queue command to client to show the print queue + - changed some print queue reporting stuff + +1.8.0: 14/10/94 + - added international chars to valid_dos_char(). Thanks + to Daniel.Grandjean@dgr.epfl.ch + - volume label fix + - released alpha1 + - important off by 4 fix in the server + - readbraw size adaption in the client + - released alpha2 + - wait3 cast for NeXt fixed. Thanks to dbrandon@politics.tamu.edu. + - man page fix for max xmit. Thanks to mmoore@wexford (Mike Moore) + - is_8_3() fixes from Jochen Roderburg <Roderburg@rrz.Uni-Koeln.DE> + - list_match() fix from jkf@soton.ac.uk + - statfs3 fix for BSDI from dan@supra.com + - changed file open/close/read in server in preparation for mmap() + based IO. + - added mmap() support for reading files in the server. Optional + at compile time. Thanks to suggestion from Roger Binns <rogerb@x.co.uk> + - mmap bug fixes + - added __SAMBA__ name in nmbd + - major changes for support of lanman2 and long filenames from + Jeremy Allison (jeremy@netcom.com) + - lseek optimisation. Thanks to Linus Torvalds. + - released alpha4 + - date patches for lanman2 from Jeremy Allison + - added protocol aliases to handle WfWg (untested) + - allow for zero params or data in reply_trans2 + - small lanman2 patches from jeremy + - more prototype additions for clean compilation + - postscript patches from tim@fsg.com + - more lanman2 patches from Jeremy + - added null ioctl support + - kanji patches from fujita@ainix.isac.co.jp + - released alpha6 + - disallowed null password access (thanks to Birger Kraegelin krg@iitb.fhg.de) + - Makefile fix for ultrix from andrew@d2bsys.demon.co.uk (Andrew Stirling) + - added per-service mangled names + - totally re-vamped loadparm.c + - added "mangling char" parameter + - released alpha7 + - added "default case = lower|upper" service option + - change mangling char to a service parameter + - ultrix enhanced security patch from steven@gopher.dosli.govt.nz + - more changes to loadparm.c + - printer name always set in [printers] + - string_free() fix thanks to jef_iwaniw@pts.mot.com + - changed group handling to be faster and work for large numbers + of groups + - added dynamic gid_t type determination + - released alpha8 + - fixed become_user() problem for services with invalid + directories + - added "invalid users" list on per service basis + - fixed pointer problems in alpha8 (thanks to murnaghant@a1uproar.yuppy.rdgmts.MTS.dec.com) + - fixed some date setting problems + - trans2 fixes from jeremy to stop infinite directory listings of + long filenames + - "standard input" lpq patch from root@tlspu.demon.co.uk (Adrian Hungate) + - changed password checking to check session list and validated ids + before user list + - split off password functions into password.c + - added hosts equiv and rhosts code (thanks to Tim Murnaghan <murnaghant@a1uproar.yuppy.hhl.MTS.dec.com>) + - released alpha11 + - added "newer" command to the client + - attempt at aix trapdoor uid workaround + - released alpha12 + - minor trans2 bugfix + - added ufc crypt (fast crypt) support. Thanks to suggestion from + forrest d whitcher <fw@world.std.com> + - socket() fix for getting bcast and netmask thanks to + Brian.Onn@Canada.Sun.COM + - added beginnings of IPC and named pipe support in the server + - changed file structure a bit, creating reply.c + - finished print queue support for lanman1 + - changed default protocol to LANMAN2 + - released alpha13 + - logged IPC connects at a higher debug level + - added netgroup support to hosts equiv search + - disallowed root access though hosts.equiv (thanks to Colin.Dean@Smallworld.co.uk) + - kanji and password handling fixes from fujita@ainix.isac.co.jp + - several bug fixes for lanman and other things from + esser@pib1.physik.uni-bonn.de (Bernd Esser) + - updated man pages, README and announce files. + - released 1.8.00alpha1 + - reply_close() time change fix from Andreas Bahrdt <100321.2431@compuserve.com> + - added valid users list to compliment invalid users list. + - aix fixes from tomc@osi.curtin.edu.au (Tom Crawley) + - changed testparm output format + - support for getting time from the server (nearly untested) + - fixed device type error for wild device ???? + - fixed groups problem when in 0 groups + - more IPC fixups + - added support for "net view \\server" command to list + available services (like browsing) + - released 1.8.00alpha2 + - changed port choice for nmbd -L + - added -L option to client to view share list on a host + - bug fixes for NetShareEnum code + - added "server string" option + - changed default print file name to include remote machine name. + - added hooks for browsing in nmbd + - added browsing to nmbd + - freebsd fixed from Steve Sims SimsS@Infi.Net + - got rid of tell() + - added subnet browsing with the S option in lmhosts + - made smbd prime nmbd with a 1 byte dgram + - added REUSADDR to open_socket_in() thanks to peter@ifm.liu.se + + +1.8.01: 18/10/94 + + - auto add group "LANGROUP" if no group specified in nmbd + - made nmbd more responsive at startup + - lots of cleanups and consistancy checks + - added -C option to nmbd to set "machine comment". + - fixed postscript option + - force print_file in print_open() + - restructured the browsing a little + - casesignames fix for lanman-dos + - auto-load home directory from session setup + - changed to StrnCpy() for safety + - fixed "out of file descriptors" bug in the client (a WfWg bug?) + + +1.8.02: 22/10/94 + - fixed uppercase username problem + - added "hide dot files" option + - changed auto debug log in nmbd + - added LMHOSTS to Makefile + - added M flag in lmhosts to specify own netbios name + - added "load printers" option to auto-load all printers + - substitution of %p in lpq command + - substitution of %h and %v in server string and -C option of + nmbd + - string substitions substitute all occurances of a pattern + - added casesignames global option + - fix for man pages thanks to David Gardiner <dgardine@cssip.edu.au> + - changed debug options a bit + - added default for lpq command and lpr command + - changed default shell path to /bin/sh + - forced lpq under api to run as root - should speed things up + - added "group" option to force group of a connection + - added "read list" and "write list" options + - added max mux option - seems to fix NT browsing? + - added "mangled map" option thanks to + Martin.Tomes@uk.co.eurotherm.controls + - separated mangling functions into mangle.c + - allowed all dos chars in mangled names + - apollo changes from Helmut Buchsbaum <buc@eze22.siemens.co.at> + - password changing code from Bob Nance <Bob.Nance@niehs.nih.gov> + it doesn't quite work yet, but it's a start (disabled by default) + + +1.8.03: 25/10/94 + - made auto loaded services browsable as per default service + so you can hide homes but keep home directories. + - changed check_name() to handle "direct to network" printing + - auto 3 minute deadtime if all connections are closed. This + prevents restart when polling the print queue. + - fix for newer command in client from Rich-Hoesly@uai.com + - changed connection recording method + - added the program smbstatus + - changed timeout mechanism + - "null passwords" option from Pim Zandbergen <pim@cti-software.nl> + - made new files with casesignames=False set their case to the default + case. + - fixed problem of uppercasing first letter of printers in printcap + - debug level fixes in trans2 from jimw@PE-Nelson.COM (Jim Watt) + - made null printer default to lp + +1.8.04: 27/10/94 + - added OS2.txt from riiber@oslonett.no + - another "auto services" fix. A silly strtok() bug :-( + - fixed the status locking and max connections (broken in 1.8.03) + - released alpha1 + - added gets_slash so lines can be continued in smb.conf and + lmhosts + - browse list bugfix + - default to "load printers=yes" + - rewrote pcap.c + - intergraph bugfix from tarjeij@ulrik.uio.no + - changed properties flags in nmbd (to fix NT print browsing) + - allowed very long lines in printcap parsing. + +1.8.05: 28/10/94 + - lanman2 fix from Jeremy + +1.9.00: 22/1/95 + - only add home if not already there. + - added ulogoffX support + - PTR_DIFF() cleanups + - fixed a bug that caused STATUS..LCK to grow very large + - changed mangling to handle names ending in . a little better + - added "strip dot" option + - SGI and setgroups() fix from bill@sg25.npt.nuwc.navy.mil + - fixed password preservation in password_ok() (again?) + - unink fix from emer@vssad.enet.dec.com (Joel S. Emer) + - changed username part of spool filename to max 10 chars (from 6) + - magic script fix from beverly@datacube.com (Beverly Brown) + - reply_special() fix from Peter Brouwer <pb@apd.dec.com> + - stopped nmbd from listening on 138. It didn't seem to help much. + - clix fixes from ttj@sknsws61.sjo.statkart.no + - fixed select behaviour under Linux + - man page fix from Robin Cutshaw <robin@intercore.com> + - ISC block size fix from ralf@rbsoft.sdata.de (Ralf Beck) + - ISC fixes from Martin.Tomes@controls.eurotherm.co.uk + - attrib bit fix in smbclient (pointed out by Rich-Hoesly@uai.com) + - japanese extensions from fujita@ainix.isac.co.jp (Takashi + Fujita) and ouki@gssm.otuska.tsukuba.ac.jp. + - SCO patches from Stephen.Rothwell@pd.necisa.oz.au + - changed the system commands to redirect stderr + - changed default printername to service name for all print ops + - added ability to delete print queue entries + - added warning if you try to print without -P in smbclient + - INTERACTIVE patches from cardinal@settimo.italtel.it + - patch to handle spaces in group names from GJC@vax1.village.com + (GEORGE J. CARRETTE) + - lockingX fix from stefank@esi.COM.AU (Stefan Kjellberg) + - some fairly radical changes to filename handling. We can now + handle mixed case filenames properly + - released alpha2 + - added sysv printing support and improved bsd support + - changed the user that does print queues and lprm jobs + - return code support in the client from doylen@nbslib.isc-br.com (Doyle Nickless) + - added "strict locking" option. Defaults to no. + + - added -I switch to nmbd + - fixed DEV bug thanks to Dirk.DeWachter@rug.ac.be + - use pw_encrypt() for shadow passords in Linux (from begemot@begemot.iko.kharkov.ua (Dmitry Gorodchanin)) + - disabled read prediction by default + - added varient handling code to ipc.c for printQ and printDel. + - released alpha5 + - AUX patches from root@dolphin.csudh.edu + - struct timeval fix from gkb1@york.ac.uk + - patches to merge ISC and INTERACTIVE from pim@cti-software.nl + - changed to "printing =" + - fixed problem with long print queues. + - fixed node status request in nmbd to go to non bcast + - made default path in services /tmp if not specified + - added %u in passwd program + - fixed up the password changing code for Linux + - no guest sess setup when user level security + - changed timeouts to kill dirptrs so cdroms can be unmounted + - added auto-reload of smb.conf if changed + - added SIGHUP to reload the config files + - added -M option to nmbd to search for a master browser + - added support for continue bit in trans2findnext + - changed to dynamic strings in some more structures + - changed default deadtime to 30 minutes + - cleaned up the memory swapping code a bit + - updated the man pages somewhat + - added %m and %u in the "path=" of services + - released alpha6 + - simple testing and fixups for solaris, sunos, aix, ultrix and + osf/1 (this is all I have access to). + - fixed chdir bug + - added hashing to cnum selection + - released alpha7 + - fixed printing bug + - reduced chance of "hung" smbd with dead client + - fixed do_match() bug (recently introduced) + - released alpha8 + - nameserver fix from W.J.M.vGeest@et.tudelft.nl (W.J.M. van Geest) + - rewrote readbraw to try and overlap reads with writes + - client optimisations + - rewrote getwd cache and enabled it by default + - added partial smb packet reads (hopefully faster writes) + - added log file and log level options (with subs) + - added "read size" option + - tried setting some more socket options + - can use subs in "config file=" and will auto-reload + - added "include" options, with some subs + - finally got print manager working with NT + - auto-respond in nmbd to non-broadcast (auto WINS server, no -A + needed) + - released alpha10 + - auto-delet unused services when reloading + - fixed auto-deletion + - fixed long names in printing + - fixed double loading of services file + - added printer file name support + - reformatted man pages for better www conversion + - renamed to 1.9.00. + - added support for RNetServerGetInfo and NetWkstaGetInfo API's + - updated the docs a bit + - released alpha1 + - added -M - + - changed nmbd announce interval to 10 mins in outgoing packets + - hopefully fixed idle timeout reconnects + - strupper all command lines in nmbd + - added %a substitution for "remote architecture" + - added "Samba" protocol (same as lanman2) + - added "security = SERVER" + - released alpha2 + - lowercase password fix + - fixed connect path length bug (thanks to JOHN YTSENG + <jtseng@cory.EECS.Berkeley.EDU>) + - added subs on "password server". + - fixed printing filename bug from smbclient + - disk quotas and hpux printing support from Dirk.DeWachter@rug.ac.be + - Makefile patches from pappinm@ayr_srv2.nth.dpi.qld.gov.au + - AFS patches from Mike Allard (mgrmja@nextwork.rose-hulman.edu) + - fixed grp name = server name problem + - man page updates from Charlie Brady (charlieb@budge.apana.org.au) + - fixed file search bug by adding "finished" flag + - added "max log size". Suggestion from Mark Hastings <mark.hastings@gain.com> + - released alpha3 + - changed the read/write routines to handle partial read/writes + - released alpha4 + - changed "guest account" to per-service + - changed so "guest ok" allows access to the guest account, + not the "user=" line + - changed default readsize to 2048 + - try bind to 137 in nmbd if possible + - added server lookup to -L option in smbclient (gets list of servers) + - added -M switch to smbclient for sending winpopup messages + - released alpha5 + - FAQ updates from Paul Blackman ictinus@lake.canberra.edu.au + +1.9.01: 23/1/95 + - changed comment in print Q info to service rather than server comment + - fixed smbclient -L to NT when in user level security mode + - hopefully finally fixed NT print manager problems + - added informative messages during smbclient -M + - added node status replies to nmbd + - changed the lock offset fixup calculation to be more friendly + to dumb lockd daemons. + - added sigbus and sigsegv handlers to catch any silly errors and + print a message + - added message receipt to smbd and "message command =" option + +1.9.02: 25/1/95 + - added argv/argc mangling for people who start the server the + wrong way. + - some man page updates + - added "revalidate" option + - added hosts allow/deny access check to messaging access + - added timeouts in the client + - added check for existance of smbrun binary + - man page updates from Colin.Dean@Smallworld.co.uk + - freebsd patches from dfr@render.com + - added mask sanity check in SMBsearch + - added more useful substitutions (%S, %P, %I and %T) + - added "exec =" option to execute commands on each connection + +1.9.03: 13/3/95 + - added "socket options" option + - close base fd's (0,1 and 2) + - use dup(0) for inetd operation + - better detection of is_daemon + - hopefully finally fixed silly put bug that gave the wrong + date on files. + - fixed segv in readbraw bug + - added improved checing for invalid (or null) print file name + - several patches from ad@papyrus.hamburg.com (Andreas Degert) + - fixed slow logout bug in smbclient + - fixed automounter problems + - added subs on lock dir + - BSDI patch from John.Terpstra@Aquasoft.com.au + - added separate nmb and smb logfile entries in the Makefile + - fixed return code error in open calls + - added simple status display of printer in lpq parsing + - rewrote the directory handling to avoid seekdir (added dir.c) + - added uid=65535 check (thanks to grant@gear.torque.net) + - enhanced transfer_file() to add header (used in readbraw) + - reply_special bugfix from ferret@pc8871.seqeb.gov.au + - added HAVE_PATHCONF + - RiscIX patches from Jim Barry <jim@ilp.com> and + Charles Gay-Jones <charlie@ilp.com> + - CLIX patches from ttj@sknsws61.sjo.statkart.no + - fixed aix lpq parser from kvintus@acd.com + - added substitutions to "include=" + - M88K_S3 patches from tonyb@plaza.ds.adp.com (Tony D. Birnseth) + - fixed mangled stack problem + - added code to handle broken readdir() setups on solaris + - initgroups() fix from jarit@to.icl.fi + - dgux dfree fix from listwork@cloud9.net + - dnix support from Peter Olsson <pol@leissner.se> + - getgrgid() patch from tpg@bailey.com (Tom Gall) + - Makefile patch from obrien@Sea.Legent.com (David O'Brien) + - password changing fixes from Dirk.DeWachter@rug.ac.be + - minor man page updates + - tried to enhance the read prediction code a little bit + +1.9.04: 16/3/95 + - a bit better handling of global include lists + - fixed GSTRING bug in loadparm.c (affected "socket options =") + - fixed broken lpq parsing code (recent bug). + Thanks to Dirk.DeWachter@rug.ac.be + +1.9.05: 20/3/95 + - improved mget in client to take multiple arguments and default + to *.* + - socket option fixes for both nmbd and smbd + - changed the byteorder handling scheme to be more portable (and + faster) + - lint cleanups from kast@kcs.planet.net (Robert Kast) + - added crude segv, sigbus and sighup recovery to nmbd + - rewrote lanman2_match to be closer to NT and WfWg behaviour + - Cray support from velo@sesun3.epfl.ch (Martin Ouwehand) + - "admin users" patch from Tim Leamy <tcleamy@ucdavis.edu> + - released alpha1 + - added samba.7 man page + - no chdir when doing non AS_USER protocols + - become_guest() returns true in trapdoor uid system + - added more sophisticated segv/sigbus reporting (Linux only) + - released alpha2 + - minor code cleanups (output of -Wall) + - smbprint fix from James Dryfoos <dryfoos@ll.mit.edu> + - improved testparm a little + - updated INSTALL.txt a little + + +1.9.06: 21/3/95 + - added %S substitution to users, valid users and invalid + users. This is useful for [homes]. + - split off printing routines into printing.c and more dir + commands into dir.c + - postexec patch from jpm@gin.Mens.DE (Jan-Piet Mens) + - smbstatus updates from jpm@gin.Mens.DE (Jan-Piet Mens) + - reload sighup after use + - fixed name ptr offset bug + - added %f in print commands + - fixed byte ordering in nmbd which caused browsing to fail in + 1.9.05 + +1.9.07: 22/3/95 + - important directory listing fix + - allowed path= in [homes] section + - printer status patches from Dirk.DeWachter@rug.ac.be + +1.9.08: 24/3/95 + - fixed . and .. in root dir for lanman2 + - better default comment in [homes] + - added time stamping to directory entries + - check directory access at connection time + - rlimit code from loebach@homer.atria.com (Thomas M. Loebach) + - fixed home dir default comment + - totally rewrote dptr handling to overcome a persistant bug + - added [globals] as well as [global] + +1.9.09: 30/3/95 + - fixed static string bug in nmbd + - better null password handling + - split CFLAGS in Makefile + - fixed typo in smbclient messaging + - made home dir not inherit path from [global] + - standard input printing patch from xiao@ic.ac.uk + - added O_CREAT to all print opens (bug in Win95) + - use /proc for process_exists under Linux and solaris + - fixed another segv problem in readbraw + - fixed volume label problem + - lots of changes to try and support the NT1 protocol + - released alpha1 + - fixed session setup bug with NT in NT1 protocol + - released alpha2 + - fixed "get" bug in smbclient that affected NT3.5 + - added SO_KEEPALIVE as a default socket option in smbd + - changed some error codes to match those that NT 3.5 produces + - updated trans2 with some new calls for Win95 and WinNT (better + long file support) + - released alpha3 + - fixed "nmbd -D -b" timeouts + - added IS_LONG_NAME flag to getattr in NT1 + - added the NT qfileinfo trans2 commands + - merged qpathinfo with qfileinfo + - changed idling technique to try and be more friendly to + clients + - merged setfileinfo with setpathinfo and updated them with the NT fns + - improved read prediction a lot + - added read prediction to readbraw + - improved fault reporting (last packet dump) + +1.9.10: 30/3/95 + - fixed read prediction+readbraw bug for read/write files + +1.9.11: 9/4/95 + - fixed trans2 qpathinfo bug + - fixed bug with % in service name when doing print queue requests + - default readsize now 16K + - minor read prediction changes + - fixed status initialisation in print queue reporting + - fixed const compile problem for hpux + - minor SMBread fix from Volker Lendecke <lendecke@namu01.gwdg.de> + - removed space after -P in print commands (for fussy systems) + - disabled level2 of setfilepathinfo + - changed to a single read dir model, saving all dir names in + the Dir structure + - disabled NT protocols in the client due to reported problems + - fixed QUERY_FS_VOLUME_INFO which caused Win95 to hang on drive + properties + - minor lseek bug fix + - fixed up keepalives + - new timezone handling code (hopefully better!) + from steve@qv3pluto.LeidenUniv.nl + - BSDI interface patch from jrb@csi.compuserve.com + - gettimeofday changes from Roger Binns <rogerb@x.co.uk> + - added smbrun option + - added "root preexec" and "root postexec" options + +1.9.12: 12/4/95 + - hopefully fixed some recently introduced NT problems + - fixed a unlink error code problem + - minor testparm fix + - fixed silly error messages about comments in config files + - added "valid chars" option for other languages + +1.9.13: 28/4/95 + - patches from David O'Brien (obrien@Sea.Legent.com) improving the + netgroup suport, and adding the "map archive" option, as well as + other minor cleanups. + - tried to add info level 3 and 4 support for OS/2 + - default deadtime set to 0 as in docs + - cleaned up the trans2 code a little + - cleaned up the Makefile a little + - added charset.c and charset.h + - expanded "valid chars" option to handle case mapping + - lots of changes to try and get timezones right + - released alpha1 + - win95 fixups + - released alpha2 + - added %H substitution (gives home directory) + - nameserv.c cleanups and minor bug fixes + - redid the browse hook logic + - fixed daylight saving time offset for logfile messages + - added name cacheing to nmbd + - added send counts to node status in nmbd + - added STRICT_TIMEZONES compile time option (very computationally + expensive) + - removed the partial read code + - cleaned up the permission checking a lot + - added share modes (DENY_READ, DENY_WRITE, DENY_ALL, DENY_NONE, + DENY_FCB and DENY_DOS) + - added "share modes" option + - cleaned up the file open calls + - released alpha4 + - fixed important one line bug in open_file() + - trans2 client fix from lendecke@namu01.gwdg.de + - netgroup patche from David O'Brien (obrien@Sea.Legent.com) + - case sensitive fix from lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis) + - got long filenames working from Win95 dos prompt + - added "workgroup=" option + - added "username map" option including multiple maps, group maps etc + - fixed password server for NT1 protocol and made it more robust + - changed unix_mode() to add IWUSR to read-only directories. This + is much closer to what clients expect. + - added preservation of unused permission bits when a chmod() is + called from a client. + - made static those fns that could be + - fixed typo in access.c (thanks to Andrew J Cole + <A.J.Cole@cbl.leeds.ac.uk>) + - added %d substitution for process id + (thanks to lenneis@statrix2.wu-wien.ac.at (Joerg Lenneis)) + - changed share error code to ERRbadshare + - added locked files list to smbstatus if share modes is enabled + - changed DENY_DOS to allow read by other tasks + - added shared_pending checks to server + - preserverd all possible permission bits during a chmod, and + fixed a trans2 chmod bug + - open /dev/null to use up first 3 fds, in an attempt to stop rogue + library routines from causing havoc + - fixed NT username problem when in server security + - added "force user" and "force group" options + - cleaned up some of the IPC calls a bit + - added writeraw to the client and cleaned up write raw in the server + - osf1 big-crypt bugfix from Udo Linauer <ul@eacpc4.tuwien.ac.at> + - hopefully better disk-full checking + - next uid bugfix from patrick@graphics.cornell.edu + - changed share modes so lock directory doesn't need to be world + writeable + - enabled write-raw by default + - added server_info() in client + - added level checks in some ipc calls + - added defines for the important timeouts in local.h + - added print queue deletion to smbclient (untested) + - removed the sysconf() calls + - optimised writebraw a bit + - fixed some file deletion problems + - added total_data check for extended attribs in trans2 (for OS/2) + - fixed broadcast reply bug in nmbd + - added careful core dumping code + - added faster password level searches (suggestion + by lydick@cvpsun104.csc.ti.com (Dan Lydick)) + + +1.9.14: 22/9/95 + - fixed up level 3 and 4 trans2 requests for OS/2 + - minor optimisations in a few places + - cleaned up the closing of low fds a bit + - added SO_REUSEADDR to socket as a daemon + - override aDIR bit for directories in dos_chmod() + - SGI5 fixes from ymd@biosym.com (Yuri Diomin) + - bsize sanity check and removed sunos force to 1k + - force the create mode to be at least 0700 + - SCO and freebsd include changes from Peter Olsson + <pol@leissner.se> + - check with FQDN in access.c (thanks to Arne Ansper <arne@ioc.ee>) + - default broadcast for dnix from Peter Olsson <pol@leissner.se> + - solaris patches from Ronald Guilmette <rfg@segfault.us.com> + - added EXDEV handling + - small AFS Makefile patch from mgrlhc@nextwork.rose-hulman.edu + - hopefully fixed the Win95 dates to work in other than my + timezone + - attempted alignment fixups (to speed up memcpy) + - added some DCE/DFS support (thanks to Jim Doyle <doyle@oec.com>) + - added fix so that root doesn't have special privilages to open + readonly files for writing (but admin users do). This fixes the MS + office install problem. + - fixed trans2 response bug in client + - got dual names working for NT + - enabled lock_and_read in NT protocol + - added %L macro for "local machine" + - changed dfree reporting to use "sectors per unit" + - fixed "not enough memory" bug in MS print manger by limiting + share name length in share enum. + - "short preserve case" option from Rabin Ezra (rabin@acm.org) + - added archive option to client + - changed openX in client to be able to open hidden and system files + - added "sync always" option + - rewrote writebmpx and readbmpx + - added auto string_sub_basic to all loadparm strings + - lots of nmbd fixups (add registration, refresh etc) + - released alpha1 + - added smbtar patches from Ricky Poulten (poultenr@logica.co.uk) + - added a lpq cache and the "lpq cache time" option + - released alpha 2 + - sun includes fix from Kimmo Suominen <kim@deshaw.com> + - change nmbd -L lookup type to workstation from server + - added min print space option + - added user and group names to smbstatus (thanks to + davide.migliavacca@inferentia.it) + - fixed %f in print command bug (thanks to huver@amgraf.com) + - added wildcard support to SMBmv + - misc patches from David Elm (delm@hookup.net) + - changed default of "share modes" to yes + - changed default of "status" to yes + - aix qconfig parsing from Jean-Pierre.Boulard@univ-rennes1.fr + - more long_date fixups + - added wildcards to nmbd + - extensive changes to ipc.c and miscellaneous other changes + from ad@papyrus.hamburg.com (Andreas Degert). Should especially + help OS/2 users + - added name release to nmbd + - relesed alpha4 + - fixed "SOLARIS" to SUNOS5 in Makefile + - several minor fixups to get it to compile on aix, osf1, ultrix, + solaris and sunos + - released alpha5 + - minor bug fixes and cleanups in ipc.c + - fixed "only user" bug + - changed lpq to report guest queue entries as sesssetup_user to + allow for deletion by windows + - released alpha6 + - added __SAMBA__ as type 0 in nmbd (was type 20) + - fixed null print job bug + - added 8 char warnings to testparm and smbclient + - changed to 8 char limit for names in pcap.c + - added linked list of config files to detect all date changes + that require a reload + - simplified pcap guessing heuristics + - added space trimming to the name mapping + - updated Get_Pwnam to add allow_change field for username mapping + - fixed MemMove bug (thanks to mass@tanner.com (Massimo + Sivilotti)) + - released alpha7 + - rewrote MemMove to be a little more efficient + - ipc va_arg bug fix from djg@tas.com (Dave Gesswein) + - added check for illegal chars in long filenames + - fixed name cache init bug in nmbd + - Convex patches from Victor Balashov <balashov@cv.jinr.dubna.su> + - timestring() bugfix from staale@spacetec.no + - changed %H to give path of forced user if one is set + - added quoting to smbclient to allow spaces in filenames + - convex and other patches from Ulrich Hahn + <ulrich.hahn@zdv.uni-tuebingen.de> + - released alpha8 + - fixed rename directory bug + - nmbd wins fix from Maximilian Errath <errath@balu.kfunigraz.ac.at> + - client and AFS changes + password.c reorganisation + "more" and + "pwd" commands in client from Todd j. Derr (tjd@smi.med.pitt.edu) + - fixed several nmbd bugs + - released alpha9 + - fixed another "cd" bug in smbclient + - password encryption from Jeremy Allison + - added "passwd chat" option and chat interpretation code + - added "smb passwd file" option + - released alpha10 + - cleaned up chgpasswd.c a little + - portability changes to the encryption handling code + - added password encryption to smbclient + - fixed a share level security encryption bug + - added "ENCRYPTION.txt" document + - released alpha11 + - added code to detect a password server loop + - fixed typo in chkpath in client.c that broken cd (again) + - LINUX_BIGCRYPT from marsj@ida.liu.se + - AFS password fixup from jbushey@primenet.com (Jeffrey G. Bushey) + - iso/8859-1 charcnv patches from Dan.Oscarsson@malmo.trab.se + - strtok/user_in_list fix from roderich@nodebonn.muc.bmw.de + - NETGROUP patches from J.W.Schilperoort@research.ptt.nl + - trim_string patch from J.W.Schilperoort@research.ptt.nl + - fixed problem with files with no extension getting mixed up + - ipc bugfix for print job deletion from Rainer Leberle <rleberle@auspex.de> + - released alpha12 + - pwlen fix in NETGROUP from Andrew J Cole <A.J.Cole@cbl.leeds.ac.uk> + - lots of uid and encryption changes from Jeremy Allison. WinDD + should now work. + - released alpha13 + - fixed max_xmit bug in client + - select fix in server (fixed critical drive errors under ISC) + - released alpha14 + - wildcard fix from Jeremy + - changes to make IPC code more robust + - small select loop change to reduce cleaning of share files + - vtp, altos and mktime patches from Christian A. Lademann + <cal@zls.com> + - EEXIST bugfix in server.c + - changed mangled map to apply in all cases + - released alpha15 + - fixed fcb open permissions (should mean apps know when a file is + read only) + - released alpha16 + - client help formatting fix and docs fix from Peter Jones + <thanatos@drealm.org> + - added a directory cache + - use /proc whenever possible for pid detection + - TCSANOW fix in getsmbpasswd from roderich@nodebonn.muc.bmw.de + - fixed default printing mode for sysv systems + - make client always expand mask + - more minor IPC fixups + - pyramid makefile entry from jeffrey@itm.org + - client fixups for passlen, maxvcs and session redirect from + Charles Hoch <hoch@hplcgh.hpl.hp.com> + - finally fixed important IPC bug (varargs bug with int16) + - quota patches from Dirk.DeWachter@rug.ac.be + - print queue cache changes (per service) and print queue priority + additions from Dirk.DeWachter@rug.ac.be + - new japanese patches (incomplete) from + fujita@ainix.isac.co.jp (Takashi Fujita) + - moved a lot more functions into system.c via wrappers + - changed a lot of the connection refused error codes to be more + informative (or at least different) + - released alpha17 + - changed error return code from cannor chdir() in make_connection + - fixed realloc() bug in printing.c + - fixed invalid username bug in sesssetupX + - released alpha18 + - made default service change name to asked for service (idea + from Ian McEwan <ijm@doc.ic.ac.uk>) + - fixed "guest only" bug + - sambatar patches from Ricky + - printing.c patches from Dirk.DeWachter@rug.ac.be + - rewrote become_user() + - sunos5 patch from Niels.Baggesen@uni-c.dk + - more japanese extensions patches from fujita@ainix.isac.co.jp + - released alpha20 + - added force_user to conn struct + + +1.9.15: 14/11/95 + - removed bcast override from workgroup announce in nmbd + - aix patch, added NO_SYSMOUNTH, from + lionel leston <102624.346@compuserve.com> + - quick fix in lp_string() to try and stop some core dumps + - added uid cache in connections structure + to make user level security faster + - changed dos_mode() to show read-only on read-only shares only if + user w bit not set + - added check to stop exit_server() looping + - core dump fix in string_sub() + - fix client bug for long dirs in NT1 mode. + Thanks to Erwin Authried (erwin@ws1.atv.tuwien.ac.at) + - switched to a safer (but probably slower) readbraw implementation + - released p1 + - readbraw fix from Stefaan.Eeckels@eunet.lu + - fixed groups bug when user is in 1 group + - fixed NT1 dir bug + - changed default protocol in client to NT1 + - changed trans2 to not return both names in long listing if long + name is 8.3 + - made stat of "" return RONLY if not writeable drive + - wrapped strcpy() to stop nulls propogating (hack) + - made rename and unlink look at share locks on file + - clitar memory leak fix from jjm@jjm.com + - added -p option to smbstatus to list smbd processes + - added rename to the client + - released p2 + - fixed SMBmv for case where the destination exists + - man page patch from michal@ellpspace.math.ualberta.ca (Michal Jaegermann) + - once again redid the time handling, but finally explained what + is going on, this is written up in TIME.txt. The "kludge-GMT" used + by NT is a bastard and led to a lot of the confusion + - kanji patch from fujita@ainix.isac.co.jp (Takashi Fujita) + - is08859-1 patches from eauth@mail.cso.co.at + - starting rewriting nmbd, new nmbd is nmbd2, old one still around + for time being + - released p3 + - rewrote more of nmbd2 to use new structures + - CLIX patches from Jason.J.Faultless@bechtel.btx400.co.uk + - DirCacheFlush() bugfix from Michael Joosten + <joost@ori.cadlab.de>. This bug explains a lot of the crashes. + - fixed a bug in ChDir() that caused reversion to / in some + situations + - ipc fix from Magnus Hyllander <mhy@os.se> + - released p4 + - smbpasswd fix from Jeremy + - compilation fixes from Magnus Hyllander <mhy@os.se> + - added NetServerEnum to ipc.c (needed for master browser stuff) + - Makefile fix from Gunther Mayer <gmayer@physik.uni-kl.de> + - cleanups for clean compile on several OSes + - added browse mastering code + - started integration with smb.conf for nmbd2 + - released p5 + - fixed death_time (should be t+ttl*3) + - fixed non-removal of dead servers + - added smbstatus -u patch from oskarh@spornet.is (Oskar Hannesson) + - NETGROUP fix from J.W.Schilperoort@research.kpn.com + - select and NO_SETGROUPS patches from lennylim@netcom.com (Lenny + Lim) + - added LINKS_READ_ONLY define in dos_mode() for LM/X + compatability + - "dir a.c" bug fixed thanks to roderich@nodebonn.muc.bmw.de + (Roderich Schupp) + - job cancel fix in client from peo@mtek.chalmers.se + - changed nmbd2 to nmbd + - fixed "dir a*" under trans2 lookups + - added StrnCaseCmp() + - updated docs a bit for new browsing stuff + - updated INSTALL.txt + - hopefully fixed server level security with WfWg + +1.9.15 (patches): + - major/minor fix for solaris from Jeroen Schipper + <Jeroen.Schipper@let.ruu.nl> + - fixed critical bug in directory listings + - released p1 + - fixed one of the causes of "out of memory" while browsing + - fixed manpage install script (Paul Blackman) + - added DNS failures to name cache + - fixed writebmpx bug (affects OS/2) + - misc OS/2 fixes, mostly for EA handling + - added SMBcopy + - added "max ttl" option + - arch detection patch from Bas Laarhoven <bas@vimec.nl> + - released p2 + - another OS/2 fix - the level 4 getpathinfo for EAs + - added "alternate permissions" option + - changed client to parse destination names into name + domain + - fixed problem with PrimaryGroup and lmhosts loading + - added domain master ability to nmbd + - added "domain master" option + - added "domain controller" option and code + - pwd fix to client from Erik Devriendt (de@te6.siemens.be) + - fixed problem in smbmv that led to ar not working in mks + - added transs2 + - released p3 + - updated email addresses + - fix for innetgr from Olaf Seibert (rhialto@polder.ubc.kun.nl) + - client translate fix from bandc@dircon.co.uk + - netbsd bcast fix from from Olaf Seibert (rhialto@polder.ubc.kun.nl) + - syslog code from Alex Nash <alex@fa.tca.com> + - strip dot fix from Arne Ansper <arne@ioc.ee> + - added addtosmbpass + man page from + michal@ellpspace.math.ualberta.ca (Michal Jaegermann) + - pcap fix for AIX from Jon Christiansen <jchristi@sctcorp.com> + - fixed servertype bug in remote announcements + - fixed up illegal name checks (should also be faster) + - kanji patches from fujita@ainix.isac.co.jp (Takashi Fujita) + - fixed bug handling non-encrypted passwords + - released p4 + - fixed makefile for addtosmbpass + - DCE/DFS fixes from John Brezak (brezak@ch.hp.com) + - client patch for partial command matching from Andrew Wiseman + <bandc@dircon.co.uk> + - made is_8_3() handle full paths + - rewrote open_file_shared() with help from Charles Hoch + <hoch@hplcgh.hpl.hp.com> + - changed syslog to handle interactive programs + - fixed syslog problem with full path in argv[0] + - illegal name fixup for kanji from fujita@ainix.isac.co.jp + - fixed server level security to allow fallback to encryption + - changed reply_read() and reply_lockread() to ignore clients + smb_bufsize in order to handle broken lanman clients + - fixed NT wildcard problem with old style programs + - man page patches from "John M. Sellens" + <jmsellen@watdragon.uwaterloo.ca> + - partially documented the "character set" option + - changed default for MAXDIR to 64 + - changed default DPTR idle time to 120 + - released p5 + - QNX patches from eldo@invisa.satlink.net (Eldo Loguzzo) + - made nmbd use the "max log size" option and changed log handling + code a bit + - sunos patches, remote protocol (%R) addition and arch detection + changes to stop compiler warning from Timothy Hunt <tim@fsg.com> + - fixed become_user() bug that led to incorrect permissions in + some situations. + - released p6 + - is_8_3() fix from Charles Hoch <hoch@hplcgh.hpl.hp.com> + - nmblib bugfix from gmk@mhcnet.att.com (George Kull) + - aix pcap fix from Jon Christiansen <jchristi@sctcorp.com> + - added explicit sig_pipe() in server.c + - added domain logins option (not fully implemented) + - added HAVE_GMTOFF code + - got rid of PM_MAXLINE + - minor client fix from goggi@eflir (Garðar Georg Nielsen) + - added SIGCLD_IGNORE for HPUX (from Tor Lillqvist + <tml@hemuli.tte.vtt.fi>) + - OSF/1 lpq patch from scooter@GENE.COM (Scooter Morris) + - NeXT patches from pmarcos@next.com (Paul Marcos) + - dstdiff patch to stop infinite loop from Erwin Authried (eauth@cso.co.at) + - password server option can now take a list of password servers + - patches to let samba run on OS/2 from Jason Rumney <jasonr@pec.co.nz> + - added domain logon and logon script suport + - SCO openserver 5 patches from Scott Michel <scottm@intime.intime.com> + - Makefile changes from Marty Leisner <leisner@sdsp.mc.xerox.com> + - chgpasswd changes from Roman Dumych <roman@nyxis.unibase.com> + for SVR4 + - GUEST_SESSSETUP change from David.Chappell@mail.cc.trincoll.edu + - released p7 + - moved SO_REUSEADDR before bind() (thanks to Thomas Bellman + <tbe@ivab.se>) + - added more flexible GUEST_SESSSETUP to local.h and restored + pre-p7 behaviour as default + - released p8 + +1.9.16: + - Makefile fix from Marty Leisner <leisner@sdsp.mc.xerox.com> + - added %g and %G substitutions + - changed IDLE_CLOSED_TIMEOUT to 60 + - fixed the "admin user" status in domain logons + - hpux 10 "trusted security" patches from David-Michael Lincke + (dlincke@sgcl1.unisg.ch) + - added nmb lookups to client from Adrian Hill <Adrian.Hill@softimage.co.uk> + - svr4 pause/resume printing patch from Brendan O'Dea (bod@tyndall.com.au) + - fixed master announcement thanks to Luke Leighton <rah14@dial.pipex.com> + - changed srcdir usage in Makefile to be friendly to more systems + - NT4 alignment patches from Jeremy Allison (jra@vantive.com) + - updated share mode code for new spec + - minor client bugfix (for smbclient '\\\') + - fix for level 260 when magling disabled. From Martin Tomes + <Martin.Tomes@ecl.etherm.co.uk> + - SMBtranss2 fix for OS/2 from Jeremy Allison + - profiles fixup from Timm Wetzel <twetzel@cage.mpibpc.gwdg.de> + - man page updates from Dirk.DeWachter@rug.ac.be + - nmbsync fix from Andy Whitcroft <andy@soi.city.ac.uk> + - Lynx patches from Manfred Woelfel <woelfel@hpesco1.fzk.de> + - new smbtar stuff from Ricky + - changed to share mode DENY_NONE for tar + - fixed -D option of smbclient when in tar mode + - added aARCH to open modes + - added code to cope with select/read errors + - fixed blank browse entries after smb.conf reread + - integrated new browse stuff from Luke into ipc.c + - added workgroup list to smbclient -L + - improved archive attribute handling in close_file() and + write_file() + - smbtar fixes from Martin.Kraemer@mch.sni.de + - Linux quota patch from xeno@mix.hsv.no + - try to work around NT passlen2 problem in session setup + - released alpha1 + + +========== +todo: + + +64 bit longs and IP addresses may give problems with unsigned longs? + +set archive bit whenever file is modified?? + +fix man page dates + +reply only to own workgroup in server enum + +patch to compile with g++ and possibly solaris c++ + +nmbd needs to keep browse list uptodate by talking to the master if it loses +an election as others may still think its a valid backup and use it to get +lists. + +leftover lock files can end up belonging to non-smbd processes after a reboot. + +hosts allow in nmbd + +hosts allow cache + +add password command in smbclient + +drag long filename to samba under os/2 gives short name + +document max ttl option + +dup/close 0 for getopt? + +implement SMBmove ?? + +add option to print more info about locked files (full path, share name +etc) + +very slow listing CD, perhaps because of order of stat and readdir or add +masking to opendir? + +protocol drop back in client to avoid openX etc. + +handle exported fat drives to a long filename capable client + +add check for existance of lpq commands etc (use stat?) + +get rid of the silly +4 and -4 by removing NBT stuff + +write-only shares + +document cnvchar stuff + +allow smbd to serve user and group lists to win95 + +document homes behaviour with WinDD + +add "hide file = *.o" "hide dir = .Foo*" "show file = xx*" type options. + +ALLOW_PASSWORD_CHANGE only compiles/works on some systems + +weird foooooooo/open.exe bug on NT + +%a detection can't detect Win95 versus WinNT + +reverse mangled maps, so (*.html *.htm) works for new files. + +install problems with w95. could be some sort of race? + +more efficient Files[] structure to handle thousands of open files + +lpd stuff: + Tony Aiuto (tony@ics.com) + +make max disk size local +
\ No newline at end of file diff --git a/source3/client/client.c b/source3/client/client.c new file mode 100644 index 0000000000..504cb5a0bb --- /dev/null +++ b/source3/client/client.c @@ -0,0 +1,4534 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB client + Copyright (C) Andrew Tridgell 1994-1995 + + 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. +*/ + +#ifdef SYSLOG +#undef SYSLOG +#endif + +#include "includes.h" +#include "nameserv.h" + +#ifndef REGISTER +#define REGISTER 0 +#endif + +pstring cur_dir = "\\"; +pstring cd_path = ""; +pstring service=""; +pstring desthost=""; +pstring myname = ""; +pstring password = ""; +pstring username=""; +pstring workgroup=WORKGROUP; +BOOL got_pass = False; +BOOL connect_as_printer = False; +BOOL connect_as_ipc = False; +extern struct in_addr bcast_ip; +static BOOL got_bcast=False; + +char cryptkey[8]; +BOOL doencrypt=False; + +extern pstring user_socket_options; + +/* 30 second timeout on most commands */ +#define CLIENT_TIMEOUT (30*1000) +#define SHORT_TIMEOUT (5*1000) + +/* value for unused fid field in trans2 secondary request */ +#define FID_UNUSED (0xFFFF) + +int name_type = 0x20; + +int max_protocol = PROTOCOL_NT1; + + +time_t newer_than = 0; +int archive_level = 0; + +extern struct in_addr myip; + +extern pstring debugf; +extern int DEBUGLEVEL; + +BOOL translation = False; + +/* clitar bits insert */ +extern void cmd_tar(); +extern void cmd_block(); +extern void cmd_tarmode(); +extern void cmd_setmode(); +extern int blocksize; +extern BOOL tar_inc; +extern BOOL tar_reset; +extern int process_tar(); +extern int tar_parseargs(); +/* clitar bits end */ + + +int cnum = 0; +int pid = 0; +int gid = 0; +int uid = 0; +int mid = 0; +int myumask = 0755; + +int max_xmit = BUFFER_SIZE; + +extern pstring scope; + +BOOL prompt = True; + +int printmode = 1; + +BOOL recurse = False; +BOOL lowercase = False; + +BOOL have_ip = False; + +struct in_addr dest_ip; + +#define SEPARATORS " \t\n\r" + +BOOL abort_mget = True; + +extern int Protocol; + +BOOL readbraw_supported = False; +BOOL writebraw_supported = False; + +pstring fileselection = ""; + +extern file_info def_finfo; + +/* timing globals */ +int get_total_size = 0; +int get_total_time_ms = 0; +int put_total_size = 0; +int put_total_time_ms = 0; + + +extern int Client; + +#define USENMB + +#ifdef KANJI +extern int coding_system; +#define CNV_LANG(s) (coding_system == DOSV_CODE?s:dos_to_unix(s, False)) +#define CNV_INPUT(s) (coding_system == DOSV_CODE?s:unix_to_dos(s, True)) +static BOOL +setup_term_code (char *code) +{ + int new; + new = interpret_coding_system (code, UNKNOWN_CODE); + if (new != UNKNOWN_CODE) { + coding_system = new; + return True; + } + return False; +} +#else +#define CNV_LANG(s) dos2unix_format(s,False) +#define CNV_INPUT(s) unix2dos_format(s,True) +#endif + +static void send_logout(void ); +BOOL reopen_connection(char *inbuf,char *outbuf); +static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir); +static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir); +static BOOL call_api(int prcnt,int drcnt,int mprcnt,int mdrcnt, + int *rprcnt,int *rdrcnt,char *param,char *data, + char **rparam,char **rdata); +static BOOL send_trans_request(char *outbuf,int trans, + char *name,int fid,int flags, + char *data,char *param,uint16 *setup, + int ldata,int lparam,int lsetup, + int mdata,int mparam,int msetup); + + +/**************************************************************************** +setup basics in a outgoing packet +****************************************************************************/ +void setup_pkt(char *outbuf) +{ + SSVAL(outbuf,smb_pid,pid); + SSVAL(outbuf,smb_uid,uid); + SSVAL(outbuf,smb_mid,mid); + if (Protocol > PROTOCOL_CORE) + { + SCVAL(outbuf,smb_flg,0x8); + SSVAL(outbuf,smb_flg2,0x1); + } +} + +/**************************************************************************** +write to a local file with CR/LF->LF translation if appropriate. return the +number taken from the buffer. This may not equal the number written. +****************************************************************************/ +static int writefile(int f, char *b, int n) +{ + int i; + + if (!translation) + return(write(f,b,n)); + + i = 0; + while (i < n) + { + if (*b == '\r' && (i<(n-1)) && *(b+1) == '\n') + { + b++;i++; + } + if (write(f, b, 1) != 1) + { + break; + } + b++; + i++; + } + + return(i); +} + +/**************************************************************************** + read from a file with LF->CR/LF translation if appropriate. return the + number read. read approx n bytes. +****************************************************************************/ +static int readfile(char *b, int size, int n, FILE *f) +{ + int i; + int c; + + if (!translation || (size != 1)) + return(fread(b,size,n,f)); + + i = 0; + while (i < n) + { + if ((c = getc(f)) == EOF) + { + break; + } + + if (c == '\n') /* change all LFs to CR/LF */ + { + b[i++] = '\r'; + n++; + } + + b[i++] = c; + } + + return(i); +} + + +/**************************************************************************** +read from a file with print translation. return the number read. read approx n +bytes. +****************************************************************************/ +static int printread(FILE *f,char *b,int n) +{ + int i; + + i = readfile(b,1, n-1,f); +#if FORMFEED + if (feof(f) && i>0) + b[i++] = '\014'; +#endif + + return(i); +} + +/**************************************************************************** +check for existance of a dir +****************************************************************************/ +static BOOL chkpath(char *path,BOOL report) +{ + fstring path2; + pstring inbuf,outbuf; + char *p; + + strcpy(path2,path); + trim_string(path2,NULL,"\\"); + if (!*path2) *path2 = '\\'; + + bzero(outbuf,smb_size); + set_message(outbuf,0,4 + strlen(path2),True); + SCVAL(outbuf,smb_com,SMBchkpth); + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,path2); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (report && CVAL(inbuf,smb_rcls) != 0) + DEBUG(2,("chkpath: %s\n",smb_errstr(inbuf))); + + return(CVAL(inbuf,smb_rcls) == 0); +} + + +/**************************************************************************** +send a message +****************************************************************************/ +static void send_message(char *inbuf,char *outbuf) +{ + int total_len = 0; + + char *p; + int grp_id; + + /* send a SMBsendstrt command */ + bzero(outbuf,smb_size); + set_message(outbuf,0,0,True); + CVAL(outbuf,smb_com) = SMBsendstrt; + SSVAL(outbuf,smb_tid,cnum); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,username); + p = skip_string(p,1); + *p++ = 4; + strcpy(p,desthost); + p = skip_string(p,1); + + set_message(outbuf,0,PTR_DIFF(p,smb_buf(outbuf)),False); + + send_smb(Client,outbuf); + + + if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) + { + printf("SMBsendstrt failed. (%s)\n",smb_errstr(inbuf)); + return; + } + + grp_id = SVAL(inbuf,smb_vwv0); + + printf("Connected. Type your message, ending it with a Control-D\n"); + + while (!feof(stdin) && total_len < 1600) + { + int maxlen = MIN(1600 - total_len,127); + pstring msg; + int l=0; + int c; + + bzero(msg,smb_size); + + for (l=0;l<maxlen && (c=fgetc(stdin))!=EOF;l++) + { + if (c == '\n') + msg[l++] = '\r'; + msg[l] = c; + } + + CVAL(outbuf,smb_com) = SMBsendtxt; + + set_message(outbuf,1,l+3,True); + + SSVAL(outbuf,smb_vwv0,grp_id); + + p = smb_buf(outbuf); + *p = 1; + SSVAL(p,1,l); + memcpy(p+3,msg,l); + + send_smb(Client,outbuf); + + + if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) + { + printf("SMBsendtxt failed (%s)\n",smb_errstr(inbuf)); + return; + } + + total_len += l; + } + + if (total_len >= 1600) + printf("the message was truncated to 1600 bytes "); + else + printf("sent %d bytes ",total_len); + + printf("(status was %d-%d)\n",CVAL(inbuf,smb_rcls),SVAL(inbuf,smb_err)); + + CVAL(outbuf,smb_com) = SMBsendend; + set_message(outbuf,1,0,False); + SSVAL(outbuf,smb_vwv0,grp_id); + + send_smb(Client,outbuf); + + + if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) + { + printf("SMBsendend failed (%s)\n",smb_errstr(inbuf)); + return; + } +} + + + +/**************************************************************************** +check the space on a device +****************************************************************************/ +static void do_dskattr(void) +{ + pstring inbuf,outbuf; + + bzero(outbuf,smb_size); + set_message(outbuf,0,0,True); + CVAL(outbuf,smb_com) = SMBdskattr; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + DEBUG(0,("Error in dskattr: %s\n",smb_errstr(inbuf))); + + DEBUG(0,("\n\t\t%d blocks of size %d. %d blocks available\n", + SVAL(inbuf,smb_vwv0), + SVAL(inbuf,smb_vwv1)*SVAL(inbuf,smb_vwv2), + SVAL(inbuf,smb_vwv3))); +} + +/**************************************************************************** +show cd/pwd +****************************************************************************/ +static void cmd_pwd(void) +{ + DEBUG(0,("Current directory is %s",CNV_LANG(service))); + DEBUG(0,("%s\n",CNV_LANG(cur_dir))); +} + + +/**************************************************************************** +change directory - inner section +****************************************************************************/ +static void do_cd(char *newdir) +{ + char *p = newdir; + pstring saved_dir; + pstring dname; + + /* Save the current directory in case the + new directory is invalid */ + strcpy(saved_dir, cur_dir); + if (*p == '\\') + strcpy(cur_dir,p); + else + strcat(cur_dir,p); + if (*(cur_dir+strlen(cur_dir)-1) != '\\') { + strcat(cur_dir, "\\"); + } + dos_clean_name(cur_dir); + strcpy(dname,cur_dir); + strcat(cur_dir,"\\"); + dos_clean_name(cur_dir); + + if (!strequal(cur_dir,"\\")) + if (!chkpath(dname,True)) + strcpy(cur_dir,saved_dir); + + strcpy(cd_path,cur_dir); +} + +/**************************************************************************** +change directory +****************************************************************************/ +static void cmd_cd(char *inbuf,char *outbuf) +{ + fstring buf; + + if (next_token(NULL,buf,NULL)) + do_cd(buf); + else + DEBUG(0,("Current directory is %s\n",CNV_LANG(cur_dir))); +} + + +/**************************************************************************** + display info about a file + ****************************************************************************/ +static void display_finfo(file_info *finfo) +{ + time_t t = finfo->mtime; /* the time is assumed to be passed as GMT */ + DEBUG(0,(" %-30s%7.7s%10d %s", + CNV_LANG(finfo->name), + attrib_string(finfo->mode), + finfo->size, + asctime(LocalTime(&t,GMT_TO_LOCAL)))); +} + +/**************************************************************************** + do a directory listing, calling fn on each file found + ****************************************************************************/ +void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir) +{ + DEBUG(5,("do_dir(%s,%x,%s)\n",Mask,attribute,BOOLSTR(recurse_dir))); + if (Protocol >= PROTOCOL_LANMAN2) + { + if (do_long_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir) > 0) + return; + } + + expand_mask(Mask,False); + do_short_dir(inbuf,outbuf,Mask,attribute,fn,recurse_dir); + return; +} + +/******************************************************************* + decide if a file should be operated on + ********************************************************************/ +static BOOL do_this_one(file_info *finfo) +{ + if (finfo->mode & aDIR) return(True); + + if (newer_than && finfo->mtime < newer_than) + return(False); + + if ((archive_level==1 || archive_level==2) && !(finfo->mode & aARCH)) + return(False); + + return(True); +} + +/**************************************************************************** +interpret a short filename structure +The length of the structure is returned +****************************************************************************/ +static int interpret_short_filename(char *p,file_info *finfo) +{ + finfo->mode = CVAL(p,21); + + /* this date is converted to GMT by make_unix_date */ + finfo->ctime = make_unix_date(p+22); + finfo->mtime = finfo->atime = finfo->ctime; + finfo->size = IVAL(p,26); + strcpy(finfo->name,p+30); + + return(DIR_STRUCT_SIZE); +} + +/**************************************************************************** +interpret a long filename structure - this is mostly guesses at the moment +The length of the structure is returned +The structure of a long filename depends on the info level. 260 is used +by NT and 2 is used by OS/2 +****************************************************************************/ +static int interpret_long_filename(int level,char *p,file_info *finfo) +{ + if (finfo) + memcpy(finfo,&def_finfo,sizeof(*finfo)); + + switch (level) + { + case 1: /* OS/2 understands this */ + if (finfo) + { + /* these dates are converted to GMT by make_unix_date */ + finfo->ctime = make_unix_date2(p+4); + finfo->atime = make_unix_date2(p+8); + finfo->mtime = make_unix_date2(p+12); + finfo->size = IVAL(p,16); + finfo->mode = CVAL(p,24); + strcpy(finfo->name,p+27); + } + return(28 + CVAL(p,26)); + + case 2: /* this is what OS/2 uses mostly */ + if (finfo) + { + /* these dates are converted to GMT by make_unix_date */ + finfo->ctime = make_unix_date2(p+4); + finfo->atime = make_unix_date2(p+8); + finfo->mtime = make_unix_date2(p+12); + finfo->size = IVAL(p,16); + finfo->mode = CVAL(p,24); + strcpy(finfo->name,p+31); + } + return(32 + CVAL(p,30)); + + /* levels 3 and 4 are untested */ + case 3: + if (finfo) + { + /* these dates are probably like the other ones */ + finfo->ctime = make_unix_date2(p+8); + finfo->atime = make_unix_date2(p+12); + finfo->mtime = make_unix_date2(p+16); + finfo->size = IVAL(p,20); + finfo->mode = CVAL(p,28); + strcpy(finfo->name,p+33); + } + return(SVAL(p,4)+4); + + case 4: + if (finfo) + { + /* these dates are probably like the other ones */ + finfo->ctime = make_unix_date2(p+8); + finfo->atime = make_unix_date2(p+12); + finfo->mtime = make_unix_date2(p+16); + finfo->size = IVAL(p,20); + finfo->mode = CVAL(p,28); + strcpy(finfo->name,p+37); + } + return(SVAL(p,4)+4); + + case 260: /* NT uses this, but also accepts 2 */ + if (finfo) + { + int ret = SVAL(p,0); + int namelen; + p += 4; /* next entry offset */ + p += 4; /* fileindex */ + + /* these dates appear to arrive in a weird way. It seems to + be localtime plus the serverzone given in the initial + connect. This is GMT when DST is not in effect and one + hour from GMT otherwise. Can this really be right?? + + I suppose this could be called kludge-GMT. Is is the GMT + you get by using the current DST setting on a different + localtime. It will be cheap to calculate, I suppose, as + no DST tables will be needed */ + + finfo->ctime = interpret_long_date(p); p += 8; + finfo->atime = interpret_long_date(p); p += 8; + finfo->mtime = interpret_long_date(p); p += 8; p += 8; + finfo->size = IVAL(p,0); p += 8; + p += 8; /* alloc size */ + finfo->mode = CVAL(p,0); p += 4; + namelen = IVAL(p,0); p += 4; + p += 4; /* EA size */ + p += 2; /* short name len? */ + p += 24; /* short name? */ + StrnCpy(finfo->name,p,namelen); + return(ret); + } + return(SVAL(p,0)); + } + + DEBUG(1,("Unknown long filename format %d\n",level)); + return(SVAL(p,0)); +} + + + + +/**************************************************************************** + act on the files in a dir listing + ****************************************************************************/ +static void dir_action(char *inbuf,char *outbuf,int attribute,file_info *finfo,BOOL recurse_dir,void (*fn)(),BOOL longdir) +{ + + if (!((finfo->mode & aDIR) == 0 && *fileselection && + !mask_match(finfo->name,fileselection,False,False)) && + !(recurse_dir && (strequal(finfo->name,".") || + strequal(finfo->name,"..")))) + { + if (recurse_dir && (finfo->mode & aDIR)) + { + pstring mask2; + pstring sav_dir; + strcpy(sav_dir,cur_dir); + strcat(cur_dir,finfo->name); + strcat(cur_dir,"\\"); + strcpy(mask2,cur_dir); + + if (!fn) + DEBUG(0,("\n%s\n",CNV_LANG(cur_dir))); + + strcat(mask2,"*"); + + if (longdir) + do_long_dir(inbuf,outbuf,mask2,attribute,fn,True); + else + do_dir(inbuf,outbuf,mask2,attribute,fn,True); + + strcpy(cur_dir,sav_dir); + } + else + { + if (fn && do_this_one(finfo)) + fn(finfo); + } + } +} + + +/**************************************************************************** + do a directory listing, calling fn on each file found + ****************************************************************************/ +static int do_short_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir) +{ + char *p; + int received = 0; + BOOL first = True; + char status[21]; + int num_asked = (max_xmit - 100)/DIR_STRUCT_SIZE; + int num_received = 0; + int i; + char *dirlist = NULL; + pstring mask; + file_info finfo; + + finfo = def_finfo; + + bzero(status,21); + + strcpy(mask,Mask); + + while (1) + { + bzero(outbuf,smb_size); + if (first) + set_message(outbuf,2,5 + strlen(mask),True); + else + set_message(outbuf,2,5 + 21,True); + +#if FFIRST + if (Protocol >= PROTOCOL_LANMAN1) + CVAL(outbuf,smb_com) = SMBffirst; + else +#endif + CVAL(outbuf,smb_com) = SMBsearch; + + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,num_asked); + SSVAL(outbuf,smb_vwv1,attribute); + + p = smb_buf(outbuf); + *p++ = 4; + + if (first) + strcpy(p,mask); + else + strcpy(p,""); + p += strlen(p) + 1; + + *p++ = 5; + if (first) + SSVAL(p,0,0); + else + { + SSVAL(p,0,21); + p += 2; + memcpy(p,status,21); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + received = SVAL(inbuf,smb_vwv0); + + DEBUG(5,("dir received %d\n",received)); + + DEBUG(6,("errstr=%s\n",smb_errstr(inbuf))); + + if (received <= 0) break; + + first = False; + + dirlist = Realloc(dirlist,(num_received + received)*DIR_STRUCT_SIZE); + + if (!dirlist) + return 0; + + p = smb_buf(inbuf) + 3; + + memcpy(dirlist+num_received*DIR_STRUCT_SIZE, + p,received*DIR_STRUCT_SIZE); + + memcpy(status,p + ((received-1)*DIR_STRUCT_SIZE),21); + + num_received += received; + + if (CVAL(inbuf,smb_rcls) != 0) break; + } + +#if FFIRST + if (!first && Protocol >= PROTOCOL_LANMAN1) + { + bzero(outbuf,smb_size); + CVAL(outbuf,smb_com) = SMBfclose; + + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + + strcpy(p,""); + p += strlen(p) + 1; + + *p++ = 5; + SSVAL(p,0,21); + p += 2; + memcpy(p,status,21); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT,False); + + if (CVAL(inbuf,smb_rcls) != 0) + DEBUG(0,("Error closing search: %s\n",smb_errstr(inbuf))); + } +#endif + + if (!fn) + for (p=dirlist,i=0;i<num_received;i++) + { + p += interpret_short_filename(p,&finfo); + display_finfo(&finfo); + } + + for (p=dirlist,i=0;i<num_received;i++) + { + p += interpret_short_filename(p,&finfo); + dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,False); + } + + if (dirlist) free(dirlist); + return(num_received); +} + +/**************************************************************************** + receive a SMB trans or trans2 response allocating the necessary memory + ****************************************************************************/ +static BOOL receive_trans_response(char *inbuf,int trans, + int *data_len,int *param_len, + char **data,char **param) +{ + int total_data=0; + int total_param=0; + int this_data,this_param; + + *data_len = *param_len = 0; + + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + show_msg(inbuf); + + /* sanity check */ + if (CVAL(inbuf,smb_com) != trans) + { + DEBUG(0,("Expected %s response, got command 0x%02x\n", + trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com))); + return(False); + } + if (CVAL(inbuf,smb_rcls) != 0) + return(False); + + /* parse out the lengths */ + total_data = SVAL(inbuf,smb_tdrcnt); + total_param = SVAL(inbuf,smb_tprcnt); + + /* allocate it */ + *data = Realloc(*data,total_data); + *param = Realloc(*param,total_param); + + while (1) + { + this_data = SVAL(inbuf,smb_drcnt); + this_param = SVAL(inbuf,smb_prcnt); + if (this_data) + memcpy(*data + SVAL(inbuf,smb_drdisp), + smb_base(inbuf) + SVAL(inbuf,smb_droff), + this_data); + if (this_param) + memcpy(*param + SVAL(inbuf,smb_prdisp), + smb_base(inbuf) + SVAL(inbuf,smb_proff), + this_param); + *data_len += this_data; + *param_len += this_param; + + /* parse out the total lengths again - they can shrink! */ + total_data = SVAL(inbuf,smb_tdrcnt); + total_param = SVAL(inbuf,smb_tprcnt); + + if (total_data <= *data_len && total_param <= *param_len) + break; + + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + show_msg(inbuf); + + /* sanity check */ + if (CVAL(inbuf,smb_com) != trans) + { + DEBUG(0,("Expected %s response, got command 0x%02x\n", + trans==SMBtrans?"SMBtrans":"SMBtrans2", CVAL(inbuf,smb_com))); + return(False); + } + if (CVAL(inbuf,smb_rcls) != 0) + return(False); + } + + return(True); +} + +/**************************************************************************** + do a directory listing, calling fn on each file found. Use the TRANSACT2 + call for long filenames + ****************************************************************************/ +static int do_long_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir) +{ + int max_matches = 512; + int info_level = Protocol<PROTOCOL_NT1?1:260; /* NT uses 260, OS/2 uses 2. Both accept 1. */ + char *p; + pstring mask; + file_info finfo; + int i; + char *dirlist = NULL; + int dirlist_len = 0; + int total_received = 0; + BOOL First = True; + char *resp_data=NULL; + char *resp_param=NULL; + int resp_data_len = 0; + int resp_param_len=0; + + int ff_resume_key = 0; + int ff_searchcount=0; + int ff_eos=0; + int ff_lastname=0; + int ff_dir_handle=0; + int loop_count = 0; + + uint16 setup; + pstring param; + + strcpy(mask,Mask); + + while (ff_eos == 0) + { + loop_count++; + if (loop_count > 200) + { + DEBUG(0,("ERROR: Looping in FIND_NEXT??\n")); + break; + } + + if (First) + { + setup = TRANSACT2_FINDFIRST; + SSVAL(param,0,attribute); /* attribute */ + SSVAL(param,2,max_matches); /* max count */ + SSVAL(param,4,8+4+2); /* resume required + close on end + continue */ + SSVAL(param,6,info_level); + SIVAL(param,8,0); + strcpy(param+12,mask); + } + else + { + setup = TRANSACT2_FINDNEXT; + SSVAL(param,0,ff_dir_handle); + SSVAL(param,2,max_matches); /* max count */ + SSVAL(param,4,info_level); + SIVAL(param,6,ff_resume_key); /* ff_resume_key */ + SSVAL(param,10,8+4+2); /* resume required + close on end + continue */ + strcpy(param+12,mask); + + DEBUG(5,("hand=0x%X resume=%d ff_lastname=%d mask=%s\n", + ff_dir_handle,ff_resume_key,ff_lastname,mask)); + } + /* ??? original code added 1 pad byte after param */ + + send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0, + NULL,param,&setup, + 0,12+strlen(mask)+1,1, + BUFFER_SIZE,10,0); + + if (!receive_trans_response(inbuf,SMBtrans2, + &resp_data_len,&resp_param_len, + &resp_data,&resp_param)) + { + DEBUG(3,("FIND%s gave %s\n",First?"FIRST":"NEXT",smb_errstr(inbuf))); + break; + } + + /* parse out some important return info */ + p = resp_param; + if (First) + { + ff_dir_handle = SVAL(p,0); + ff_searchcount = SVAL(p,2); + ff_eos = SVAL(p,4); + ff_lastname = SVAL(p,8); + } + else + { + ff_searchcount = SVAL(p,0); + ff_eos = SVAL(p,2); + ff_lastname = SVAL(p,6); + } + + if (ff_searchcount == 0) + break; + + /* point to the data bytes */ + p = resp_data; + + /* we might need the lastname for continuations */ + if (ff_lastname > 0) + { + switch(info_level) + { + case 260: + ff_resume_key =0; + StrnCpy(mask,p+ff_lastname,resp_data_len-ff_lastname); + /* strcpy(mask,p+ff_lastname+94); */ + break; + case 1: + strcpy(mask,p + ff_lastname + 1); + ff_resume_key = 0; + break; + } + } + else + strcpy(mask,""); + + /* and add them to the dirlist pool */ + dirlist = Realloc(dirlist,dirlist_len + resp_data_len); + + if (!dirlist) + { + DEBUG(0,("Failed to expand dirlist\n")); + break; + } + + /* put in a length for the last entry, to ensure we can chain entries + into the next packet */ + { + char *p2; + for (p2=p,i=0;i<(ff_searchcount-1);i++) + p2 += interpret_long_filename(info_level,p2,NULL); + SSVAL(p2,0,resp_data_len - PTR_DIFF(p2,p)); + } + + /* grab the data for later use */ + memcpy(dirlist+dirlist_len,p,resp_data_len); + dirlist_len += resp_data_len; + + total_received += ff_searchcount; + + if (resp_data) free(resp_data); resp_data = NULL; + if (resp_param) free(resp_param); resp_param = NULL; + + DEBUG(3,("received %d entries (eos=%d resume=%d)\n", + ff_searchcount,ff_eos,ff_resume_key)); + + First = False; + } + + if (!fn) + for (p=dirlist,i=0;i<total_received;i++) + { + p += interpret_long_filename(info_level,p,&finfo); + display_finfo(&finfo); + } + + for (p=dirlist,i=0;i<total_received;i++) + { + p += interpret_long_filename(info_level,p,&finfo); + dir_action(inbuf,outbuf,attribute,&finfo,recurse_dir,fn,True); + } + + /* free up the dirlist buffer */ + if (dirlist) free(dirlist); + return(total_received); +} + + +/**************************************************************************** + get a directory listing + ****************************************************************************/ +static void cmd_dir(char *inbuf,char *outbuf) +{ + int attribute = aDIR | aSYSTEM | aHIDDEN; + pstring mask; + fstring buf; + char *p=buf; + + strcpy(mask,cur_dir); + if(mask[strlen(mask)-1]!='\\') + strcat(mask,"\\"); + + if (next_token(NULL,buf,NULL)) + { + if (*p == '\\') + strcpy(mask,p); + else + strcat(mask,p); + } + else { + strcat(mask,"*"); + } + + do_dir(inbuf,outbuf,mask,attribute,NULL,recurse); + + do_dskattr(); +} + + + +/**************************************************************************** + get a file from rname to lname + ****************************************************************************/ +static void do_get(char *rname,char *lname,file_info *finfo1) +{ + int handle=0,fnum; + uint32 nread=0; + char *p; + BOOL newhandle = False; + char *inbuf,*outbuf; + file_info finfo; + BOOL close_done = False; + BOOL ignore_close_error = False; + char *dataptr=NULL; + int datalen=0; + + struct timeval tp_start; + GetTimeOfDay(&tp_start); + + if (finfo1) + finfo = *finfo1; + else + finfo = def_finfo; + + if (lowercase) + strlower(lname); + + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + bzero(outbuf,smb_size); + set_message(outbuf,15,1 + strlen(rname),True); + + CVAL(outbuf,smb_com) = SMBopenX; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv2,1); + SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4)); + SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN); + SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN); + SSVAL(outbuf,smb_vwv8,1); + + p = smb_buf(outbuf); + strcpy(p,rname); + p = skip_string(p,1); + + /* do a chained openX with a readX? */ +#if 1 + if (finfo.size > 0) + { + DEBUG(3,("Chaining readX wth openX\n")); + SSVAL(outbuf,smb_vwv0,SMBreadX); + SSVAL(outbuf,smb_vwv1,smb_offset(p,outbuf)); + bzero(p,200); + p -= smb_wct; + SSVAL(p,smb_wct,10); + SSVAL(p,smb_vwv0,0xFF); + SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size)); + SSVAL(p,smb_vwv9,MIN(BUFFER_SIZE,finfo.size)); + smb_setlen(outbuf,smb_len(outbuf)+11*2+1); + } +#endif + + if(!strcmp(lname,"-")) + handle = fileno(stdout); + else + { + handle = creat(lname,0644); + newhandle = True; + } + if (handle < 0) + { + DEBUG(0,("Error opening local file %s\n",lname)); + free(inbuf);free(outbuf); + return; + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + if (CVAL(inbuf,smb_rcls) == ERRSRV && + SVAL(inbuf,smb_err) == ERRnoresource && + reopen_connection(inbuf,outbuf)) + { + do_get(rname,lname,finfo1); + return; + } + DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname))); + if(newhandle) + close(handle); + free(inbuf);free(outbuf); + return; + } + + strcpy(finfo.name,rname); + + if (!finfo1) + { + finfo.mode = SVAL(inbuf,smb_vwv3); + /* these times arrive as LOCAL time, using the DST offset + corresponding to that time, we convert them to GMT */ + finfo.mtime = make_unix_date3(inbuf+smb_vwv4); + finfo.atime = finfo.ctime = finfo.mtime; + finfo.size = IVAL(inbuf,smb_vwv6); + } + + DEBUG(3,("file %s attrib 0x%X\n",CNV_LANG(finfo.name),finfo.mode)); + + fnum = SVAL(inbuf,smb_vwv2); + + /* we might have got some data from a chained readX */ + if (SVAL(inbuf,smb_vwv0) == SMBreadX) + { + p = (smb_base(inbuf)+SVAL(inbuf,smb_vwv1)) - smb_wct; + datalen = SVAL(p,smb_vwv5); + dataptr = smb_base(inbuf) + SVAL(p,smb_vwv6); + } + else + { + dataptr = NULL; + datalen = 0; + } + + + DEBUG(2,("getting file %s of size %d bytes as %s ", + CNV_LANG(finfo.name), + finfo.size, + lname)); + + while (nread < finfo.size && !close_done) + { + int method = -1; + static BOOL can_chain_close = True; + + p=NULL; + + DEBUG(3,("nread=%d max_xmit=%d fsize=%d\n",nread,max_xmit,finfo.size)); + + /* 3 possible read types. readbraw if a large block is required. + readX + close if not much left and read if neither is supported */ + + /* we might have already read some data from a chained readX */ + if (dataptr && datalen>0) + method=3; + + /* if we can finish now then readX+close */ + if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) && + ((finfo.size - nread) < + (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300)))) + method = 0; + + /* if we support readraw then use that */ + if (method<0 && readbraw_supported) + method = 1; + + /* if we can then use readX */ + if (method<0 && (Protocol >= PROTOCOL_LANMAN1)) + method = 2; + + switch (method) + { + /* use readX */ + case 0: + case 2: + if (method == 0) + close_done = True; + + /* use readX + close */ + bzero(outbuf,smb_size); + set_message(outbuf,10,0,True); + CVAL(outbuf,smb_com) = SMBreadX; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + if (close_done) + { + CVAL(outbuf,smb_vwv0) = SMBclose; + SSVAL(outbuf,smb_vwv1,smb_offset(smb_buf(outbuf),outbuf)); + } + else + CVAL(outbuf,smb_vwv0) = 0xFF; + + SSVAL(outbuf,smb_vwv2,fnum); + SIVAL(outbuf,smb_vwv3,nread); + SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread)); + SSVAL(outbuf,smb_vwv6,0); + SIVAL(outbuf,smb_vwv7,0); + SSVAL(outbuf,smb_vwv9,MIN(BUFFER_SIZE,finfo.size-nread)); + + if (close_done) + { + p = smb_buf(outbuf); + bzero(p,9); + + CVAL(p,0) = 3; + SSVAL(p,1,fnum); + SIVALS(p,3,-1); + + /* now set the total packet length */ + smb_setlen(outbuf,smb_len(outbuf)+9); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf))); + break; + } + + if (close_done && + SVAL(inbuf,smb_vwv0) != SMBclose) + { + /* NOTE: WfWg sometimes just ignores the chained + command! This seems to break the spec? */ + DEBUG(3,("Rejected chained close?\n")); + close_done = False; + can_chain_close = False; + ignore_close_error = True; + } + + datalen = SVAL(inbuf,smb_vwv5); + dataptr = smb_base(inbuf) + SVAL(inbuf,smb_vwv6); + break; + + /* use readbraw */ + case 1: + { + static int readbraw_size = BUFFER_SIZE; + + extern int Client; + bzero(outbuf,smb_size); + set_message(outbuf,8,0,True); + CVAL(outbuf,smb_com) = SMBreadbraw; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + SSVAL(outbuf,smb_vwv0,fnum); + SIVAL(outbuf,smb_vwv1,nread); + SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size)); + SSVAL(outbuf,smb_vwv4,0); + SIVALS(outbuf,smb_vwv5,-1); + send_smb(Client,outbuf); + + /* Now read the raw data into the buffer and write it */ + if(read_smb_length(Client,inbuf,0) == -1) { + DEBUG(0,("Failed to read length in readbraw\n")); + exit(1); + } + + /* Even though this is not an smb message, smb_len + returns the generic length of an smb message */ + datalen = smb_len(inbuf); + + if (datalen == 0) + { + /* we got a readbraw error */ + DEBUG(4,("readbraw error - reducing size\n")); + readbraw_size = (readbraw_size * 9) / 10; + + if (readbraw_size < max_xmit) + { + DEBUG(0,("disabling readbraw\n")); + readbraw_supported = False; + } + + dataptr=NULL; + continue; + } + + if(read_data(Client,inbuf,datalen) != datalen) { + DEBUG(0,("Failed to read data in readbraw\n")); + exit(1); + } + dataptr = inbuf; + } + break; + + case 3: + /* we've already read some data with a chained readX */ + break; + + default: + /* use plain read */ + bzero(outbuf,smb_size); + set_message(outbuf,5,0,True); + CVAL(outbuf,smb_com) = SMBread; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread)); + SIVAL(outbuf,smb_vwv2,nread); + SSVAL(outbuf,smb_vwv4,finfo.size - nread); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf))); + break; + } + + datalen = SVAL(inbuf,smb_vwv0); + dataptr = smb_buf(inbuf) + 3; + break; + } + + if (writefile(handle,dataptr,datalen) != datalen) + { + DEBUG(0,("Error writing local file\n")); + break; + } + + nread += datalen; + if (datalen == 0) + { + DEBUG(0,("Error reading file %s. Got %d bytes\n",CNV_LANG(rname),nread)); + break; + } + + dataptr=NULL; + datalen=0; + } + + + + if (!close_done) + { + bzero(outbuf,smb_size); + set_message(outbuf,3,0,True); + CVAL(outbuf,smb_com) = SMBclose; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SIVALS(outbuf,smb_vwv1,-1); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf))); + if(newhandle) + close(handle); + free(inbuf);free(outbuf); + return; + } + } + + if(newhandle) + close(handle); + + if (archive_level >= 2 && (finfo.mode & aARCH)) { + bzero(outbuf,smb_size); + set_message(outbuf,8,strlen(rname)+4,True); + CVAL(outbuf,smb_com) = SMBsetatr; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + SSVAL(outbuf,smb_vwv0,finfo.mode & ~(aARCH)); + SIVALS(outbuf,smb_vwv1,0); + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,rname); + p += strlen(p)+1; + *p++ = 4; + *p = 0; + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + } + + { + struct timeval tp_end; + int this_time; + + GetTimeOfDay(&tp_end); + this_time = + (tp_end.tv_sec - tp_start.tv_sec)*1000 + + (tp_end.tv_usec - tp_start.tv_usec)/1000; + get_total_time_ms += this_time; + get_total_size += finfo.size; + + DEBUG(2,("(%g kb/s) (average %g kb/s)\n", + finfo.size / (1.024*this_time + 1.0e-4), + get_total_size / (1.024*get_total_time_ms))); + } + + free(inbuf);free(outbuf); +} + + +/**************************************************************************** + get a file + ****************************************************************************/ +static void cmd_get(void) +{ + pstring lname; + pstring rname; + char *p; + + strcpy(rname,cur_dir); + strcat(rname,"\\"); + + p = rname + strlen(rname); + + if (!next_token(NULL,p,NULL)) { + DEBUG(0,("get <filename>\n")); + return; + } + strcpy(lname,p); + dos_clean_name(rname); + + next_token(NULL,lname,NULL); + + do_get(rname,lname,NULL); +} + + +/**************************************************************************** + do a mget operation on one file + ****************************************************************************/ +static void do_mget(file_info *finfo) +{ + pstring rname; + pstring quest; + + if (strequal(finfo->name,".") || strequal(finfo->name,"..")) + return; + + if (abort_mget) + { + DEBUG(0,("mget aborted\n")); + return; + } + + if (finfo->mode & aDIR) + sprintf(quest,"Get directory %s? ",CNV_LANG(finfo->name)); + else + sprintf(quest,"Get file %s? ",CNV_LANG(finfo->name)); + + if (prompt && !yesno(quest)) return; + + if (finfo->mode & aDIR) + { + pstring saved_curdir; + pstring mget_mask; + char *inbuf,*outbuf; + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + strcpy(saved_curdir,cur_dir); + + strcat(cur_dir,finfo->name); + strcat(cur_dir,"\\"); + + unix_format(finfo->name); + { + if (lowercase) + strlower(finfo->name); + + if (!directory_exist(finfo->name,NULL) && + sys_mkdir(finfo->name,0777) != 0) + { + DEBUG(0,("failed to create directory %s\n",CNV_LANG(finfo->name))); + strcpy(cur_dir,saved_curdir); + free(inbuf);free(outbuf); + return; + } + + if (sys_chdir(finfo->name) != 0) + { + DEBUG(0,("failed to chdir to directory %s\n",CNV_LANG(finfo->name))); + strcpy(cur_dir,saved_curdir); + free(inbuf);free(outbuf); + return; + } + } + + strcpy(mget_mask,cur_dir); + strcat(mget_mask,"*"); + + do_dir((char *)inbuf,(char *)outbuf, + mget_mask,aSYSTEM | aHIDDEN | aDIR,do_mget,False); + chdir(".."); + strcpy(cur_dir,saved_curdir); + free(inbuf);free(outbuf); + } + else + { + strcpy(rname,cur_dir); + strcat(rname,finfo->name); + do_get(rname,finfo->name,finfo); + } +} + +/**************************************************************************** +view the file using the pager +****************************************************************************/ +static void cmd_more(void) +{ + fstring rname,lname,tmpname,pager_cmd; + char *pager; + + strcpy(rname,cur_dir); + strcat(rname,"\\"); + sprintf(tmpname,"/tmp/smbmore.%d",getpid()); + strcpy(lname,tmpname); + + if (!next_token(NULL,rname+strlen(rname),NULL)) { + DEBUG(0,("more <filename>\n")); + return; + } + dos_clean_name(rname); + + do_get(rname,lname,NULL); + + pager=getenv("PAGER"); + sprintf(pager_cmd,"%s %s",(pager? pager:PAGER), tmpname); + system(pager_cmd); + unlink(tmpname); +} + + + +/**************************************************************************** +do a mget command +****************************************************************************/ +static void cmd_mget(char *inbuf,char *outbuf) +{ + int attribute = aSYSTEM | aHIDDEN; + pstring mget_mask; + fstring buf; + char *p=buf; + + *mget_mask = 0; + + if (recurse) + attribute |= aDIR; + + abort_mget = False; + + while (next_token(NULL,p,NULL)) + { + strcpy(mget_mask,cur_dir); + if(mget_mask[strlen(mget_mask)-1]!='\\') + strcat(mget_mask,"\\"); + + if (*p == '\\') + strcpy(mget_mask,p); + else + strcat(mget_mask,p); + do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False); + } + + if (! *mget_mask) + { + strcpy(mget_mask,cur_dir); + if(mget_mask[strlen(mget_mask)-1]!='\\') + strcat(mget_mask,"\\"); + strcat(mget_mask,"*"); + do_dir((char *)inbuf,(char *)outbuf,mget_mask,attribute,do_mget,False); + } +} + +/**************************************************************************** +make a directory of name "name" +****************************************************************************/ +static BOOL do_mkdir(char *name) +{ + char *p; + char *inbuf,*outbuf; + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return False; + } + + bzero(outbuf,smb_size); + set_message(outbuf,0,2 + strlen(name),True); + + CVAL(outbuf,smb_com) = SMBmkdir; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,name); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s making remote directory %s\n", + smb_errstr(inbuf),CNV_LANG(name))); + + free(inbuf);free(outbuf); + return(False); + } + + free(inbuf);free(outbuf); + return(True); +} + + +/**************************************************************************** + make a directory + ****************************************************************************/ +static void cmd_mkdir(char *inbuf,char *outbuf) +{ + pstring mask; + fstring buf; + char *p=buf; + + strcpy(mask,cur_dir); + + if (!next_token(NULL,p,NULL)) + { + if (!recurse) + DEBUG(0,("mkdir <dirname>\n")); + return; + } + strcat(mask,p); + + if (recurse) + { + pstring ddir; + pstring ddir2; + *ddir2 = 0; + + strcpy(ddir,mask); + trim_string(ddir,".",NULL); + p = strtok(ddir,"/\\"); + while (p) + { + strcat(ddir2,p); + if (!chkpath(ddir2,False)) + { + do_mkdir(ddir2); + } + strcat(ddir2,"\\"); + p = strtok(NULL,"/\\"); + } + } + else + do_mkdir(mask); +} + + +/******************************************************************* + write to a file using writebraw + ********************************************************************/ +static int smb_writeraw(char *outbuf,int fnum,int pos,char *buf,int n) +{ + extern int Client; + pstring inbuf; + + bzero(outbuf,smb_size); + bzero(inbuf,smb_size); + set_message(outbuf,Protocol>PROTOCOL_COREPLUS?12:10,0,True); + + CVAL(outbuf,smb_com) = SMBwritebraw; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,n); + SIVAL(outbuf,smb_vwv3,pos); + SSVAL(outbuf,smb_vwv7,1); + + send_smb(Client,outbuf); + + if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) + return(0); + + _smb_setlen(buf-4,n); /* HACK! XXXX */ + + if (write_socket(Client,buf-4,n+4) != n+4) + return(0); + + if (!receive_smb(Client,inbuf,CLIENT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) { + DEBUG(0,("Error writing remote file (2)\n")); + return(0); + } + return(SVAL(inbuf,smb_vwv0)); +} + + + +/******************************************************************* + write to a file + ********************************************************************/ +static int smb_writefile(char *outbuf,int fnum,int pos,char *buf,int n) +{ + pstring inbuf; + + if (writebraw_supported && n > (max_xmit-200)) + return(smb_writeraw(outbuf,fnum,pos,buf,n)); + + bzero(outbuf,smb_size); + bzero(inbuf,smb_size); + set_message(outbuf,5,n + 3,True); + + CVAL(outbuf,smb_com) = SMBwrite; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,n); + SIVAL(outbuf,smb_vwv2,pos); + SSVAL(outbuf,smb_vwv4,0); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,n); + + memcpy(smb_buf(outbuf)+3,buf,n); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) { + DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf))); + return(0); + } + return(SVAL(inbuf,smb_vwv0)); +} + + + +/**************************************************************************** + put a single file + ****************************************************************************/ +static void do_put(char *rname,char *lname,file_info *finfo) +{ + int fnum; + FILE *f; + int nread=0; + char *p; + char *inbuf,*outbuf; + time_t close_time = finfo->mtime; + char *buf=NULL; + static int maxwrite=0; + + struct timeval tp_start; + GetTimeOfDay(&tp_start); + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + bzero(outbuf,smb_size); + set_message(outbuf,3,2 + strlen(rname),True); + + if (finfo->mtime == 0 || finfo->mtime == -1) + finfo->mtime = finfo->atime = finfo->ctime = time(NULL); + + CVAL(outbuf,smb_com) = SMBcreate; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,finfo->mode); + put_dos_date3(outbuf,smb_vwv1,finfo->mtime); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,rname); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname))); + + free(inbuf);free(outbuf);if (buf) free(buf); + return; + } + + f = fopen(lname,"r"); + + if (!f) + { + DEBUG(0,("Error opening local file %s\n",lname)); + free(inbuf);free(outbuf); + return; + } + + + fnum = SVAL(inbuf,smb_vwv0); + if (finfo->size < 0) + finfo->size = file_size(lname); + + DEBUG(1,("putting file %s of size %d bytes as %s ",lname,finfo->size,CNV_LANG(rname))); + + if (!maxwrite) + maxwrite = writebraw_supported?MAX(max_xmit,BUFFER_SIZE):(max_xmit-200); + + while (nread < finfo->size) + { + int n = maxwrite; + int ret; + + n = MIN(n,finfo->size - nread); + + buf = (char *)Realloc(buf,n+4); + + fseek(f,nread,SEEK_SET); + if ((n = readfile(buf+4,1,n,f)) < 1) + { + DEBUG(0,("Error reading local file\n")); + break; + } + + ret = smb_writefile(outbuf,fnum,nread,buf+4,n); + + if (n != ret) { + if (!maxwrite) { + DEBUG(0,("Error writing file\n")); + break; + } else { + maxwrite /= 2; + continue; + } + } + + nread += n; + } + + + + bzero(outbuf,smb_size); + set_message(outbuf,3,0,True); + CVAL(outbuf,smb_com) = SMBclose; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + put_dos_date3(outbuf,smb_vwv1,close_time); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf),CNV_LANG(rname))); + fclose(f); + free(inbuf);free(outbuf); + if (buf) free(buf); + return; + } + + + fclose(f); + free(inbuf);free(outbuf); + if (buf) free(buf); + + { + struct timeval tp_end; + int this_time; + + GetTimeOfDay(&tp_end); + this_time = + (tp_end.tv_sec - tp_start.tv_sec)*1000 + + (tp_end.tv_usec - tp_start.tv_usec)/1000; + put_total_time_ms += this_time; + put_total_size += finfo->size; + + DEBUG(2,("(%g kb/s) (average %g kb/s)\n", + finfo->size / (1.024*this_time + 1.0e-4), + put_total_size / (1.024*put_total_time_ms))); + } +} + + + +/**************************************************************************** + put a file + ****************************************************************************/ +static void cmd_put(void) +{ + pstring lname; + pstring rname; + fstring buf; + char *p=buf; + file_info finfo; + finfo = def_finfo; + + strcpy(rname,cur_dir); + strcat(rname,"\\"); + + + if (!next_token(NULL,p,NULL)) + { + DEBUG(0,("put <filename>\n")); + return; + } + strcpy(lname,p); + + if (next_token(NULL,p,NULL)) + strcat(rname,p); + else + strcat(rname,lname); + + dos_clean_name(rname); + + { + struct stat st; + if (!file_exist(lname,&st)) { + DEBUG(0,("%s does not exist\n",lname)); + return; + } + finfo.mtime = st.st_mtime; + } + + do_put(rname,lname,&finfo); +} + +/**************************************************************************** + seek in a directory/file list until you get something that doesn't start with + the specified name + ****************************************************************************/ +static BOOL seek_list(FILE *f,char *name) +{ + pstring s; + while (!feof(f)) + { + if (fscanf(f,"%s",s) != 1) return(False); + trim_string(s,"./",NULL); + if (strncmp(s,name,strlen(name)) != 0) + { + strcpy(name,s); + return(True); + } + } + + return(False); +} + + +/**************************************************************************** + set the file selection mask + ****************************************************************************/ +static void cmd_select(void) +{ + strcpy(fileselection,""); + next_token(NULL,fileselection,NULL); +} + + +/**************************************************************************** + mput some files + ****************************************************************************/ +static void cmd_mput(void) +{ + pstring lname; + pstring rname; + file_info finfo; + fstring buf; + char *p=buf; + + finfo = def_finfo; + + + while (next_token(NULL,p,NULL)) + { + struct stat st; + pstring cmd; + pstring tmpname; + FILE *f; + + sprintf(tmpname,"/tmp/ls.smb.%d",(int)getpid()); + if (recurse) + sprintf(cmd,"find . -name \"%s\" -print > %s",p,tmpname); + else + sprintf(cmd,"/bin/ls %s > %s",p,tmpname); + system(cmd); + + f = fopen(tmpname,"r"); + if (!f) continue; + + while (!feof(f)) + { + pstring quest; + + if (fscanf(f,"%s",lname) != 1) break; + trim_string(lname,"./",NULL); + + again1: + + /* check if it's a directory */ + if (directory_exist(lname,&st)) + { + if (!recurse) continue; + sprintf(quest,"Put directory %s? ",lname); + if (prompt && !yesno(quest)) + { + strcat(lname,"/"); + if (!seek_list(f,lname)) + break; + goto again1; + } + + strcpy(rname,cur_dir); + strcat(rname,lname); + if (!do_mkdir(rname)) + { + strcat(lname,"/"); + if (!seek_list(f,lname)) + break; + goto again1; + } + + continue; + } + else + { + sprintf(quest,"Put file %s? ",lname); + if (prompt && !yesno(quest)) continue; + + strcpy(rname,cur_dir); + strcat(rname,lname); + } + dos_format(rname); + + /* null size so do_put knows to ignore it */ + finfo.size = -1; + + /* set the date on the file */ + finfo.mtime = st.st_mtime; + + do_put(rname,lname,&finfo); + } + fclose(f); + unlink(tmpname); + } +} + +/**************************************************************************** + cancel a print job + ****************************************************************************/ +static void do_cancel(int job) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + pstring param; + + bzero(param,sizeof(param)); + + p = param; + SSVAL(p,0,81); /* api number */ + p += 2; + strcpy(p,"W"); + p = skip_string(p,1); + strcpy(p,""); + p = skip_string(p,1); + SSVAL(p,0,job); + p += 2; + + if (call_api(PTR_DIFF(p,param),0, + 6,1000, + &rprcnt,&rdrcnt, + param,NULL, + &rparam,&rdata)) + { + int res = SVAL(rparam,0); + + if (!res) + printf("Job %d cancelled\n",job); + else + printf("Error %d calcelling job %d\n",res,job); + return; + } + else + printf("Server refused cancel request\n"); + + if (rparam) free(rparam); + if (rdata) free(rdata); + + return; +} + + +/**************************************************************************** + cancel a print job + ****************************************************************************/ +static void cmd_cancel(char *inbuf,char *outbuf ) +{ + fstring buf; + int job; + + if (!connect_as_printer) + { + DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n")); + DEBUG(0,("Trying to cancel print jobs without -P may fail\n")); + } + + if (!next_token(NULL,buf,NULL)) { + printf("cancel <jobid> ...\n"); + return; + } + do { + job = atoi(buf); + do_cancel(job); + } while (next_token(NULL,buf,NULL)); +} + + +/**************************************************************************** + get info on a file + ****************************************************************************/ +static void cmd_stat(char *inbuf,char *outbuf) +{ + fstring buf; + pstring param; + char *resp_data=NULL; + char *resp_param=NULL; + int resp_data_len = 0; + int resp_param_len=0; + char *p; + uint16 setup = TRANSACT2_QPATHINFO; + + if (!next_token(NULL,buf,NULL)) { + printf("stat <file>\n"); + return; + } + + bzero(param,6); + SSVAL(param,0,4); /* level */ + p = param+6; + strcpy(p,cur_dir); + strcat(p,buf); + + send_trans_request(outbuf,SMBtrans2,NULL,FID_UNUSED,0, + NULL,param,&setup, + 0,6 + strlen(p)+1,1, + BUFFER_SIZE,2,0); + + receive_trans_response(inbuf,SMBtrans2, + &resp_data_len,&resp_param_len, + &resp_data,&resp_param); + + if (resp_data) free(resp_data); resp_data = NULL; + if (resp_param) free(resp_param); resp_param = NULL; +} + + +/**************************************************************************** + print a file + ****************************************************************************/ +static void cmd_print(char *inbuf,char *outbuf ) +{ + int fnum; + FILE *f = NULL; + uint32 nread=0; + pstring lname; + pstring rname; + char *p; + + if (!connect_as_printer) + { + DEBUG(0,("WARNING: You didn't use the -P option to smbclient.\n")); + DEBUG(0,("Trying to print without -P may fail\n")); + } + + if (!next_token(NULL,lname,NULL)) + { + DEBUG(0,("print <filename>\n")); + return; + } + + strcpy(rname,lname); + p = strrchr(rname,'/'); + if (p) + { + pstring tname; + strcpy(tname,p+1); + strcpy(rname,tname); + } + + if ((int)strlen(rname) > 14) + rname[14] = 0; + + if (strequal(lname,"-")) + { + f = stdin; + strcpy(rname,"stdin"); + } + + dos_clean_name(rname); + + bzero(outbuf,smb_size); + set_message(outbuf,2,2 + strlen(rname),True); + + CVAL(outbuf,smb_com) = SMBsplopen; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,printmode); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,rname); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s opening printer for %s\n",smb_errstr(inbuf),CNV_LANG(rname))); + return; + } + + if (!f) + f = fopen(lname,"r"); + if (!f) + { + DEBUG(0,("Error opening local file %s\n",lname)); + return; + } + + + fnum = SVAL(inbuf,smb_vwv0); + + DEBUG(1,("printing file %s as %s\n",lname,CNV_LANG(rname))); + + while (!feof(f)) + { + int n; + + bzero(outbuf,smb_size); + set_message(outbuf,1,3,True); + + /* for some strange reason the OS/2 print server can't handle large + packets when printing. weird */ + n = MIN(1024,max_xmit-(smb_len(outbuf)+4)); + + if (translation) + n = printread(f,smb_buf(outbuf)+3,(int)(0.95*n)); + else + n = readfile(smb_buf(outbuf)+3,1,n,f); + if (n <= 0) + { + DEBUG(0,("read gave %d\n",n)); + break; + } + + smb_setlen(outbuf,smb_len(outbuf) + n); + + CVAL(outbuf,smb_com) = SMBsplwr; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,n+3); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,n); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s printing remote file\n",smb_errstr(inbuf))); + break; + } + + nread += n; + } + + DEBUG(2,("%d bytes printed\n",nread)); + + bzero(outbuf,smb_size); + set_message(outbuf,1,0,True); + CVAL(outbuf,smb_com) = SMBsplclose; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s closing print file\n",smb_errstr(inbuf))); + if (f != stdin) + fclose(f); + return; + } + + if (f != stdin) + fclose(f); +} + +/**************************************************************************** +print a file +****************************************************************************/ +static void cmd_queue(char *inbuf,char *outbuf ) +{ + int count; + char *p; + + bzero(outbuf,smb_size); + set_message(outbuf,2,0,True); + + CVAL(outbuf,smb_com) = SMBsplretq; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,32); /* a max of 20 entries is to be shown */ + SSVAL(outbuf,smb_vwv1,0); /* the index into the queue */ + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s obtaining print queue\n",smb_errstr(inbuf))); + return; + } + + count = SVAL(inbuf,smb_vwv0); + p = smb_buf(inbuf) + 3; + if (count <= 0) + { + DEBUG(0,("No entries in the print queue\n")); + return; + } + + { + char status[20]; + + DEBUG(0,("Job Name Size Status\n")); + + while (count--) + { + switch (CVAL(p,4)) + { + case 0x01: sprintf(status,"held or stopped"); break; + case 0x02: sprintf(status,"printing"); break; + case 0x03: sprintf(status,"awaiting print"); break; + case 0x04: sprintf(status,"in intercept"); break; + case 0x05: sprintf(status,"file had error"); break; + case 0x06: sprintf(status,"printer error"); break; + default: sprintf(status,"unknown"); break; + } + + DEBUG(0,("%-6d %-16.16s %-9d %s\n", + SVAL(p,5),p+12,IVAL(p,7),status)); + p += 28; + } + } + +} + + +/**************************************************************************** +delete some files +****************************************************************************/ +static void do_del(file_info *finfo) +{ + char *p; + char *inbuf,*outbuf; + pstring mask; + + strcpy(mask,cur_dir); + strcat(mask,finfo->name); + + if (finfo->mode & aDIR) + return; + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + bzero(outbuf,smb_size); + set_message(outbuf,1,2 + strlen(mask),True); + + CVAL(outbuf,smb_com) = SMBunlink; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,0); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,mask); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + DEBUG(0,("%s deleting remote file %s\n",smb_errstr(inbuf),CNV_LANG(mask))); + + free(inbuf);free(outbuf); + +} + +/**************************************************************************** +delete some files +****************************************************************************/ +static void cmd_del(char *inbuf,char *outbuf ) +{ + pstring mask; + fstring buf; + int attribute = aSYSTEM | aHIDDEN; + + if (recurse) + attribute |= aDIR; + + strcpy(mask,cur_dir); + + if (!next_token(NULL,buf,NULL)) + { + DEBUG(0,("del <filename>\n")); + return; + } + strcat(mask,buf); + + do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_del,False); +} + + +/**************************************************************************** +remove a directory +****************************************************************************/ +static void cmd_rmdir(char *inbuf,char *outbuf ) +{ + pstring mask; + fstring buf; + char *p; + + strcpy(mask,cur_dir); + + if (!next_token(NULL,buf,NULL)) + { + DEBUG(0,("rmdir <dirname>\n")); + return; + } + strcat(mask,buf); + + bzero(outbuf,smb_size); + set_message(outbuf,0,2 + strlen(mask),True); + + CVAL(outbuf,smb_com) = SMBrmdir; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,mask); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s removing remote directory file %s\n",smb_errstr(inbuf),CNV_LANG(mask))); + return; + } + +} + +/**************************************************************************** +rename some files +****************************************************************************/ +static void cmd_rename(char *inbuf,char *outbuf ) +{ + pstring src,dest; + fstring buf,buf2; + char *p; + + strcpy(src,cur_dir); + strcpy(dest,cur_dir); + + if (!next_token(NULL,buf,NULL) || !next_token(NULL,buf2,NULL)) + { + DEBUG(0,("rename <src> <dest>\n")); + return; + } + strcat(src,buf); + strcat(dest,buf2); + + bzero(outbuf,smb_size); + set_message(outbuf,1,4 + strlen(src) + strlen(dest),True); + + CVAL(outbuf,smb_com) = SMBmv; + SSVAL(outbuf,smb_tid,cnum); + SSVAL(outbuf,smb_vwv0,aHIDDEN | aDIR | aSYSTEM); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,src); + p = skip_string(p,1); + *p++ = 4; + strcpy(p,dest); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s renaming files\n",smb_errstr(inbuf))); + return; + } + +} + + +/**************************************************************************** +toggle the prompt flag +****************************************************************************/ +static void cmd_prompt(void) +{ + prompt = !prompt; + DEBUG(2,("prompting is now %s\n",prompt?"on":"off")); +} + + +/**************************************************************************** +set the newer than time +****************************************************************************/ +static void cmd_newer(void) +{ + fstring buf; + BOOL ok; + struct stat sbuf; + + ok = next_token(NULL,buf,NULL); + if (ok && (sys_stat(buf,&sbuf) == 0)) + { + newer_than = sbuf.st_mtime; + DEBUG(1,("Getting files newer than %s", + asctime(LocalTime(&newer_than,GMT_TO_LOCAL)))); + } + else + newer_than = 0; + + if (ok && newer_than == 0) + DEBUG(0,("Error setting newer-than time\n")); +} + +/**************************************************************************** +set the archive level +****************************************************************************/ +static void cmd_archive(void) +{ + fstring buf; + + if (next_token(NULL,buf,NULL)) { + archive_level = atoi(buf); + } else + DEBUG(0,("Archive level is %d\n",archive_level)); +} + +/**************************************************************************** +toggle the lowercaseflag +****************************************************************************/ +static void cmd_lowercase(void) +{ + lowercase = !lowercase; + DEBUG(2,("filename lowercasing is now %s\n",lowercase?"on":"off")); +} + + + + +/**************************************************************************** +toggle the recurse flag +****************************************************************************/ +static void cmd_recurse(void) +{ + recurse = !recurse; + DEBUG(2,("directory recursion is now %s\n",recurse?"on":"off")); +} + +/**************************************************************************** +toggle the translate flag +****************************************************************************/ +static void cmd_translate(void) +{ + translation = !translation; + DEBUG(2,("CR/LF<->LF and print text translation now %s\n", + translation?"on":"off")); +} + + +/**************************************************************************** +do a printmode command +****************************************************************************/ +static void cmd_printmode(void) +{ + fstring buf; + fstring mode; + + if (next_token(NULL,buf,NULL)) + { + if (strequal(buf,"text")) + printmode = 0; + else + { + if (strequal(buf,"graphics")) + printmode = 1; + else + printmode = atoi(buf); + } + } + + switch(printmode) + { + case 0: + strcpy(mode,"text"); + break; + case 1: + strcpy(mode,"graphics"); + break; + default: + sprintf(mode,"%d",printmode); + break; + } + + DEBUG(2,("the printmode is now %s\n",mode)); +} + +/**************************************************************************** +do the lcd command +****************************************************************************/ +static void cmd_lcd(void) +{ + fstring buf; + pstring d; + + if (next_token(NULL,buf,NULL)) + sys_chdir(buf); + DEBUG(2,("the local directory is now %s\n",GetWd(d))); +} + + +/**************************************************************************** +send a session request +****************************************************************************/ +static BOOL send_session_request(char *inbuf,char *outbuf) +{ + fstring dest; + char *p; + int len = 4; + /* send a session request (RFC 8002) */ + + strcpy(dest,desthost); + p = strchr(dest,'.'); + if (p) *p = 0; + + /* put in the destination name */ + p = outbuf+len; + name_mangle(dest,p,name_type); + len += name_len(p); + + /* and my name */ + p = outbuf+len; + name_mangle(myname,p,0); + len += name_len(p); + + /* setup the packet length */ + _smb_setlen(outbuf,len); + CVAL(outbuf,0) = 0x81; + + send_smb(Client,outbuf); + DEBUG(5,("Sent session request\n")); + + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,0) == 0x84) /* C. Hoch 9/14/95 Start */ + { + /* For information, here is the response structure. + * We do the byte-twiddling to for portability. + struct RetargetResponse{ + unsigned char type; + unsigned char flags; + int16 length; + int32 ip_addr; + int16 port; + }; + */ + extern int Client; + int port = (CVAL(inbuf,8)<<8)+CVAL(inbuf,9); + /* SESSION RETARGET */ + putip((char *)&dest_ip,inbuf+4); + + close_sockets(); + Client = open_socket_out(SOCK_STREAM, &dest_ip, port); + if (Client == -1) + return False; + + DEBUG(3,("Retargeted\n")); + + set_socket_options(Client,user_socket_options); + + /* Try again */ + return send_session_request(inbuf,outbuf); + } /* C. Hoch 9/14/95 End */ + + + if (CVAL(inbuf,0) != 0x82) + { + int ecode = CVAL(inbuf,4); + DEBUG(0,("Session request failed (%d,%d) with myname=%s destname=%s\n", + CVAL(inbuf,0),ecode,myname,desthost)); + switch (ecode) + { + case 0x80: + DEBUG(0,("Not listening on called name\n")); + DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost)); + DEBUG(0,("You may find the -I option useful for this\n")); + break; + case 0x81: + DEBUG(0,("Not listening for calling name\n")); + DEBUG(0,("Try to connect as another name (instead of %s)\n",myname)); + DEBUG(0,("You may find the -n option useful for this\n")); + break; + case 0x82: + DEBUG(0,("Called name not present\n")); + DEBUG(0,("Try to connect to another name (instead of %s)\n",desthost)); + DEBUG(0,("You may find the -I option useful for this\n")); + break; + case 0x83: + DEBUG(0,("Called name present, but insufficient resources\n")); + DEBUG(0,("Perhaps you should try again later?\n")); + break; + default: + DEBUG(0,("Unspecified error 0x%X\n",ecode)); + DEBUG(0,("Your server software is being unfriendly\n")); + break; + } + return(False); + } + return(True); +} + + +/**************************************************************************** +send a login command +****************************************************************************/ +static BOOL send_login(char *inbuf,char *outbuf,BOOL start_session,BOOL use_setup) +{ + BOOL was_null = (!inbuf && !outbuf); + int sesskey=0; + time_t servertime = 0; + extern int serverzone; + int sec_mode=0; + int crypt_len; + int max_vcs=0; + struct { + int prot; + char *name; + } + prots[] = + { + {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"}, + {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"}, + {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"}, + {PROTOCOL_LANMAN1,"LANMAN1.0"}, + {PROTOCOL_LANMAN2,"LM1.2X002"}, + {PROTOCOL_LANMAN2,"Samba"}, + {PROTOCOL_NT1,"NT LM 0.12"}, + {PROTOCOL_NT1,"NT LANMAN 1.0"}, + {-1,NULL} + }; + char *pass = NULL; + pstring dev; + char *p; + int numprots; + + if (was_null) + { + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + } + +#if AJT + if (strstr(service,"IPC$")) connect_as_ipc = True; +#endif + + strcpy(dev,"A:"); + if (connect_as_printer) + strcpy(dev,"LPT1:"); + if (connect_as_ipc) + strcpy(dev,"IPC"); + + + if (start_session && !send_session_request(inbuf,outbuf)) + { + if (was_null) + { + free(inbuf); + free(outbuf); + } + return(False); + } + + bzero(outbuf,smb_size); + + /* setup the protocol strings */ + { + int plength; + + for (plength=0,numprots=0; + prots[numprots].name && prots[numprots].prot<=max_protocol; + numprots++) + plength += strlen(prots[numprots].name)+2; + + set_message(outbuf,0,plength,True); + + p = smb_buf(outbuf); + for (numprots=0; + prots[numprots].name && prots[numprots].prot<=max_protocol; + numprots++) + { + *p++ = 2; + strcpy(p,prots[numprots].name); + p += strlen(p) + 1; + } + } + + CVAL(outbuf,smb_com) = SMBnegprot; + setup_pkt(outbuf); + + CVAL(smb_buf(outbuf),0) = 2; + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + show_msg(inbuf); + + if (CVAL(inbuf,smb_rcls) != 0 || ((int)SVAL(inbuf,smb_vwv0) >= numprots)) + { + DEBUG(0,("SMBnegprot failed. myname=%s destname=%s - %s \n", + myname,desthost,smb_errstr(inbuf))); + if (was_null) + { + free(inbuf); + free(outbuf); + } + return(False); + } + + Protocol = prots[SVAL(inbuf,smb_vwv0)].prot; + + + if (Protocol < PROTOCOL_NT1) { + sec_mode = SVAL(inbuf,smb_vwv1); + max_xmit = SVAL(inbuf,smb_vwv2); + sesskey = IVAL(inbuf,smb_vwv6); + serverzone = SVALS(inbuf,smb_vwv10)*60; + /* this time is converted to GMT by make_unix_date */ + servertime = make_unix_date(inbuf+smb_vwv8); + if (Protocol >= PROTOCOL_COREPLUS) { + readbraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x1) != 0); + writebraw_supported = ((SVAL(inbuf,smb_vwv5) & 0x2) != 0); + } + crypt_len = smb_buflen(inbuf); + memcpy(cryptkey,smb_buf(inbuf),8); + DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv3))); + max_vcs = SVAL(inbuf,smb_vwv4); + DEBUG(3,("max vcs %d\n",max_vcs)); + DEBUG(3,("max blk %d\n",SVAL(inbuf,smb_vwv5))); + } else { + /* NT protocol */ + sec_mode = CVAL(inbuf,smb_vwv1); + max_xmit = IVAL(inbuf,smb_vwv3+1); + sesskey = IVAL(inbuf,smb_vwv7+1); + serverzone = SVALS(inbuf,smb_vwv15+1)*60; + /* this time arrives in real GMT */ + servertime = interpret_long_date(inbuf+smb_vwv11+1); + crypt_len = CVAL(inbuf,smb_vwv16+1); + memcpy(cryptkey,smb_buf(inbuf),8); + if (IVAL(inbuf,smb_vwv9+1) & 1) + readbraw_supported = writebraw_supported = True; + DEBUG(3,("max mux %d\n",SVAL(inbuf,smb_vwv1+1))); + max_vcs = SVAL(inbuf,smb_vwv2+1); + DEBUG(3,("max vcs %d\n",max_vcs)); + DEBUG(3,("max raw %d\n",IVAL(inbuf,smb_vwv5+1))); + DEBUG(3,("capabilities 0x%x\n",IVAL(inbuf,smb_vwv9+1))); + } + + DEBUG(3,("Sec mode %d\n",SVAL(inbuf,smb_vwv1))); + DEBUG(3,("max xmt %d\n",max_xmit)); + DEBUG(3,("Got %d byte crypt key\n",crypt_len)); + DEBUG(3,("Chose protocol [%s]\n",prots[SVAL(inbuf,smb_vwv0)].name)); + + doencrypt = ((sec_mode & 2) != 0); + + if (servertime) { + static BOOL done_time = False; + if (!done_time) { + DEBUG(1,("Server time is %sTimezone is UTC%+02.1f\n", + asctime(LocalTime(&servertime,GMT_TO_LOCAL)), + -(double)(serverzone/3600.0))); + done_time = True; + } + } + + get_pass: + + if (got_pass) + pass = password; + else + pass = (char *)getpass("Password: "); + + if (Protocol >= PROTOCOL_LANMAN1 && use_setup) + { + fstring pword; + int passlen = strlen(pass)+1; + strcpy(pword,pass); + +#ifdef SMB_PASSWD + if (doencrypt && *pass) { + DEBUG(3,("Using encrypted passwords\n")); + passlen = 24; + SMBencrypt(pass,cryptkey,pword); + } +#else + doencrypt = False; +#endif + + /* if in share level security then don't send a password now */ + if (!(sec_mode & 1)) {strcpy(pword, "");passlen=1;} + + /* send a session setup command */ + bzero(outbuf,smb_size); + + if (Protocol < PROTOCOL_NT1) { + set_message(outbuf,10,1 + strlen(username) + passlen,True); + CVAL(outbuf,smb_com) = SMBsesssetupX; + setup_pkt(outbuf); + + CVAL(outbuf,smb_vwv0) = 0xFF; + SSVAL(outbuf,smb_vwv2,max_xmit); + SSVAL(outbuf,smb_vwv3,2); + SSVAL(outbuf,smb_vwv4,max_vcs-1); + SIVAL(outbuf,smb_vwv5,sesskey); + SSVAL(outbuf,smb_vwv7,passlen); + p = smb_buf(outbuf); + memcpy(p,pword,passlen); + p += passlen; + strcpy(p,username); + } else { + if (!doencrypt) passlen--; + /* for Win95 */ + set_message(outbuf,13,0,True); + CVAL(outbuf,smb_com) = SMBsesssetupX; + setup_pkt(outbuf); + + CVAL(outbuf,smb_vwv0) = 0xFF; + SSVAL(outbuf,smb_vwv2,BUFFER_SIZE); + SSVAL(outbuf,smb_vwv3,2); + SSVAL(outbuf,smb_vwv4,getpid()); + SIVAL(outbuf,smb_vwv5,sesskey); + SSVAL(outbuf,smb_vwv7,passlen); + SSVAL(outbuf,smb_vwv8,0); + p = smb_buf(outbuf); + memcpy(p,pword,passlen); p += SVAL(outbuf,smb_vwv7); + strcpy(p,username);p = skip_string(p,1); + strcpy(p,workgroup);p = skip_string(p,1); + strcpy(p,"Unix");p = skip_string(p,1); + strcpy(p,"Samba");p = skip_string(p,1); + set_message(outbuf,13,PTR_DIFF(p,smb_buf(outbuf)),False); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + show_msg(inbuf); + + if (CVAL(inbuf,smb_rcls) != 0) + { + if (! *pass && + ((CVAL(inbuf,smb_rcls) == ERRDOS && + SVAL(inbuf,smb_err) == ERRnoaccess) || + (CVAL(inbuf,smb_rcls) == ERRSRV && + SVAL(inbuf,smb_err) == ERRbadpw))) + { + got_pass = False; + DEBUG(3,("resending login\n")); + goto get_pass; + } + + DEBUG(0,("Session setup failed for username=%s myname=%s destname=%s %s\n", + username,myname,desthost,smb_errstr(inbuf))); + DEBUG(0,("You might find the -U, -W or -n options useful\n")); + DEBUG(0,("Sometimes you have to use `-n USERNAME' (particularly with OS/2)\n")); + DEBUG(0,("Some servers also insist on uppercase-only passwords\n")); + if (was_null) + { + free(inbuf); + free(outbuf); + } + return(False); + } + + if (Protocol >= PROTOCOL_NT1) { + char *domain,*os,*lanman; + p = smb_buf(inbuf); + os = p; + lanman = skip_string(os,1); + domain = skip_string(lanman,1); + if (*domain || *os || *lanman) + DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",domain,os,lanman)); + } + + /* use the returned uid from now on */ + if (SVAL(inbuf,smb_uid) != uid) + DEBUG(3,("Server gave us a UID of %d. We gave %d\n", + SVAL(inbuf,smb_uid),uid)); + uid = SVAL(inbuf,smb_uid); + } + + /* now we've got a connection - send a tcon message */ + bzero(outbuf,smb_size); + + if (strncmp(service,"\\\\",2) != 0) + { + DEBUG(0,("\nWarning: Your service name doesn't start with \\\\. This is probably incorrect.\n")); + DEBUG(0,("Perhaps try replacing each \\ with \\\\ on the command line?\n\n")); + } + + + again2: + + { + int passlen = strlen(pass)+1; + fstring pword; + strcpy(pword,pass); + +#ifdef SMB_PASSWD + if (doencrypt && *pass) { + passlen=24; + SMBencrypt(pass,cryptkey,pword); + } +#endif + + /* if in user level security then don't send a password now */ + if ((sec_mode & 1)) { + strcpy(pword, ""); passlen=1; + } + + set_message(outbuf,4,2 + strlen(service) + passlen + strlen(dev),True); + CVAL(outbuf,smb_com) = SMBtconX; + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv3,passlen); + + p = smb_buf(outbuf); + memcpy(p,pword,passlen); + p += passlen; + strcpy(p,service); + p = skip_string(p,1); + strcpy(p,dev); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + /* trying again with a blank password */ + if (CVAL(inbuf,smb_rcls) != 0 && + (int)strlen(pass) > 0 && + !doencrypt && + Protocol >= PROTOCOL_LANMAN1) + { + DEBUG(2,("first SMBtconX failed, trying again. %s\n",smb_errstr(inbuf))); + strcpy(pass,""); + goto again2; + } + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("SMBtconX failed. %s\n",smb_errstr(inbuf))); + DEBUG(0,("Perhaps you are using the wrong sharename, username or password?\n")); + DEBUG(0,("Some servers insist that these be in uppercase\n")); + if (was_null) + { + free(inbuf); + free(outbuf); + } + return(False); + } + + + max_xmit = MIN(max_xmit,BUFFER_SIZE-4); + if (max_xmit <= 0) + max_xmit = BUFFER_SIZE - 4; + + cnum = SVAL(inbuf,smb_tid); + + DEBUG(3,("Connected with cnum=%d max_xmit=%d\n",cnum,max_xmit)); + + if (was_null) + { + free(inbuf); + free(outbuf); + } + return True; +} + + +/**************************************************************************** +send a logout command +****************************************************************************/ +static void send_logout(void ) +{ + pstring inbuf,outbuf; + + bzero(outbuf,smb_size); + set_message(outbuf,0,0,True); + CVAL(outbuf,smb_com) = SMBtdis; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,SHORT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("SMBtdis failed %s\n",smb_errstr(inbuf))); + } + + +#ifdef STATS + stats_report(); +#endif + exit(0); +} + + + +/**************************************************************************** +call a remote api +****************************************************************************/ +static BOOL call_api(int prcnt,int drcnt, + int mprcnt,int mdrcnt, + int *rprcnt,int *rdrcnt, + char *param,char *data, + char **rparam,char **rdata) +{ + static char *inbuf=NULL; + static char *outbuf=NULL; + + if (!inbuf) inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + if (!outbuf) outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + send_trans_request(outbuf,SMBtrans,"\\PIPE\\LANMAN",0,0, + data,param,NULL, + drcnt,prcnt,0, + mdrcnt,mprcnt,0); + + return (receive_trans_response(inbuf,SMBtrans, + rdrcnt,rprcnt, + rdata,rparam)); +} + +/**************************************************************************** + send a SMB trans or trans2 request + ****************************************************************************/ +static BOOL send_trans_request(char *outbuf,int trans, + char *name,int fid,int flags, + char *data,char *param,uint16 *setup, + int ldata,int lparam,int lsetup, + int mdata,int mparam,int msetup) +{ + int i; + int this_ldata,this_lparam; + int tot_data=0,tot_param=0; + char *outdata,*outparam; + pstring inbuf; + char *p; + + this_lparam = MIN(lparam,max_xmit - (500+lsetup*SIZEOFWORD)); /* hack */ + this_ldata = MIN(ldata,max_xmit - (500+lsetup*SIZEOFWORD+this_lparam)); + + bzero(outbuf,smb_size); + set_message(outbuf,14+lsetup,0,True); + CVAL(outbuf,smb_com) = trans; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + outparam = smb_buf(outbuf)+(trans==SMBtrans ? strlen(name)+1 : 3); + outdata = outparam+this_lparam; + + /* primary request */ + SSVAL(outbuf,smb_tpscnt,lparam); /* tpscnt */ + SSVAL(outbuf,smb_tdscnt,ldata); /* tdscnt */ + SSVAL(outbuf,smb_mprcnt,mparam); /* mprcnt */ + SSVAL(outbuf,smb_mdrcnt,mdata); /* mdrcnt */ + SCVAL(outbuf,smb_msrcnt,msetup); /* msrcnt */ + SSVAL(outbuf,smb_flags,flags); /* flags */ + SIVAL(outbuf,smb_timeout,0); /* timeout */ + SSVAL(outbuf,smb_pscnt,this_lparam); /* pscnt */ + SSVAL(outbuf,smb_psoff,smb_offset(outparam,outbuf)); /* psoff */ + SSVAL(outbuf,smb_dscnt,this_ldata); /* dscnt */ + SSVAL(outbuf,smb_dsoff,smb_offset(outdata,outbuf)); /* dsoff */ + SCVAL(outbuf,smb_suwcnt,lsetup); /* suwcnt */ + for (i=0;i<lsetup;i++) /* setup[] */ + SSVAL(outbuf,smb_setup+i*SIZEOFWORD,setup[i]); + p = smb_buf(outbuf); + if (trans==SMBtrans) + strcpy(p,name); /* name[] */ + else + { + *p++ = 0; /* put in a null smb_name */ + *p++ = 'D'; *p++ = ' '; /* this was added because OS/2 does it */ + } + if (this_lparam) /* param[] */ + memcpy(outparam,param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data,this_ldata); + set_message(outbuf,14+lsetup, /* wcnt, bcc */ + PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False); + + show_msg(outbuf); + send_smb(Client,outbuf); + + if (this_ldata < ldata || this_lparam < lparam) + { + /* receive interim response */ + if (!receive_smb(Client,inbuf,SHORT_TIMEOUT) || CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s request failed (%s)\n", + trans==SMBtrans?"SMBtrans":"SMBtrans2", smb_errstr(inbuf))); + return(False); + } + + tot_data = this_ldata; + tot_param = this_lparam; + + while (tot_data < ldata || tot_param < lparam) + { + this_lparam = MIN(lparam-tot_param,max_xmit - 500); /* hack */ + this_ldata = MIN(ldata-tot_data,max_xmit - (500+this_lparam)); + + set_message(outbuf,trans==SMBtrans?8:9,0,True); + CVAL(outbuf,smb_com) = trans==SMBtrans ? SMBtranss : SMBtranss2; + + outparam = smb_buf(outbuf); + outdata = outparam+this_lparam; + + /* secondary request */ + SSVAL(outbuf,smb_tpscnt,lparam); /* tpscnt */ + SSVAL(outbuf,smb_tdscnt,ldata); /* tdscnt */ + SSVAL(outbuf,smb_spscnt,this_lparam); /* pscnt */ + SSVAL(outbuf,smb_spsoff,smb_offset(outparam,outbuf)); /* psoff */ + SSVAL(outbuf,smb_spsdisp,tot_param); /* psdisp */ + SSVAL(outbuf,smb_sdscnt,this_ldata); /* dscnt */ + SSVAL(outbuf,smb_sdsoff,smb_offset(outdata,outbuf)); /* dsoff */ + SSVAL(outbuf,smb_sdsdisp,tot_data); /* dsdisp */ + if (trans==SMBtrans2) + SSVAL(outbuf,smb_sfid,fid); /* fid */ + if (this_lparam) /* param[] */ + memcpy(outparam,param,this_lparam); + if (this_ldata) /* data[] */ + memcpy(outdata,data,this_ldata); + set_message(outbuf,trans==SMBtrans?8:9, /* wcnt, bcc */ + PTR_DIFF(outdata+this_ldata,smb_buf(outbuf)),False); + + show_msg(outbuf); + send_smb(Client,outbuf); + + tot_data += this_ldata; + tot_param += this_lparam; + } + } + + return(True); +} + +/**************************************************************************** +try and browse available connections on a host +****************************************************************************/ +static BOOL browse_host(BOOL sort) +{ +#ifdef NOSTRCASECMP +#define strcasecmp StrCaseCmp +#endif + extern int strcasecmp(); + + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + pstring param; + int count = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = param; + SSVAL(p,0,0); /* api number */ + p += 2; + strcpy(p,"WrLeh"); + p = skip_string(p,1); + strcpy(p,"B13BWz"); + p = skip_string(p,1); + SSVAL(p,0,1); + SSVAL(p,2,BUFFER_SIZE); + p += 4; + + if (call_api(PTR_DIFF(p,param),0, + 1024,BUFFER_SIZE, + &rprcnt,&rdrcnt, + param,NULL, + &rparam,&rdata)) + { + int res = SVAL(rparam,0); + int converter=SVAL(rparam,2); + int i; + BOOL long_share_name=False; + + if (res == 0) + { + count=SVAL(rparam,4); + p = rdata; + + if (count > 0) + { + printf("\n\tSharename Type Comment\n"); + printf("\t--------- ---- -------\n"); + } + + if (sort) + qsort(p,count,20,QSORT_CAST strcasecmp); + + for (i=0;i<count;i++) + { + char *sname = p; + int type = SVAL(p,14); + int comment_offset = IVAL(p,16) & 0xFFFF; + fstring typestr; + *typestr=0; + + switch (type) + { + case STYPE_DISKTREE: + strcpy(typestr,"Disk"); break; + case STYPE_PRINTQ: + strcpy(typestr,"Printer"); break; + case STYPE_DEVICE: + strcpy(typestr,"Device"); break; + case STYPE_IPC: + strcpy(typestr,"IPC"); break; + } + + printf("\t%-15.15s%-10.10s%s\n", + sname, + typestr, + comment_offset?rdata+comment_offset-converter:""); + + if (strlen(sname)>8) long_share_name=True; + + p += 20; + } + + if (long_share_name) { + printf("\nNOTE: There were share names longer than 8 chars.\nOn older clients these may not be accessible or may give browsing errors\n"); + } + } + } + + if (rparam) free(rparam); + if (rdata) free(rdata); + + return(count>0); +} + + +/**************************************************************************** +get some server info +****************************************************************************/ +static void server_info() +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + int rdrcnt,rprcnt; + pstring param; + + bzero(param,sizeof(param)); + + p = param; + SSVAL(p,0,63); /* api number */ + p += 2; + strcpy(p,"WrLh"); + p = skip_string(p,1); + strcpy(p,"zzzBBzz"); + p = skip_string(p,1); + SSVAL(p,0,10); /* level 10 */ + SSVAL(p,2,1000); + p += 6; + + if (call_api(PTR_DIFF(p,param),0, + 6,1000, + &rprcnt,&rdrcnt, + param,NULL, + &rparam,&rdata)) + { + int res = SVAL(rparam,0); + int converter=SVAL(rparam,2); + + if (res == 0) + { + p = rdata; + + printf("\nServer=[%s] User=[%s] Workgroup=[%s] Domain=[%s]\n", + rdata+SVAL(p,0)-converter, + rdata+SVAL(p,4)-converter, + rdata+SVAL(p,8)-converter, + rdata+SVAL(p,14)-converter); + } + } + + if (rparam) free(rparam); + if (rdata) free(rdata); + + return; +} + + +/**************************************************************************** +try and browse available connections on a host +****************************************************************************/ +static BOOL list_servers() +{ + char *rparam = NULL; + char *rdata = NULL; + int rdrcnt,rprcnt; + char *p; + pstring param; + int uLevel = 1; + int count = 0; + + /* now send a SMBtrans command with api ServerEnum? */ + p = param; + SSVAL(p,0,0x68); /* api number */ + p += 2; + strcpy(p,"WrLehDO"); + p = skip_string(p,1); + + strcpy(p,"B16BBDz"); +#if 0 + strcpy(p,getenv("XX_STR2")); +#endif + + p = skip_string(p,1); + SSVAL(p,0,uLevel); + SSVAL(p,2,0x2000); /* buf length */ + p += 4; + + SIVAL(p,0,SV_TYPE_ALL); + + if (call_api(PTR_DIFF(p+4,param),0, + 8,10000, + &rprcnt,&rdrcnt, + param,NULL, + &rparam,&rdata)) + { + int res = SVAL(rparam,0); + int converter=SVAL(rparam,2); + int i; + + if (res == 0) { + char *p2 = rdata; + count=SVAL(rparam,4); + + if (count > 0) { + printf("\n\nThis machine has a browse list:\n"); + printf("\n\tServer Comment\n"); + printf("\t--------- -------\n"); + } + + for (i=0;i<count;i++) { + char *sname = p2; + int comment_offset = IVAL(p2,22) & 0xFFFF; + printf("\t%-16.16s %s\n", + sname, + comment_offset?rdata+comment_offset-converter:""); + + p2 += 26; + } + } + } + + if (rparam) {free(rparam); rparam = NULL;} + if (rdata) {free(rdata); rdata = NULL;} + + SIVAL(p,0,SV_TYPE_DOMAIN_ENUM); + + if (call_api(PTR_DIFF(p+4,param),0, + 8,10000, + &rprcnt,&rdrcnt, + param,NULL, + &rparam,&rdata)) + { + int res = SVAL(rparam,0); + int converter=SVAL(rparam,2); + int i; + + if (res == 0) { + char *p2 = rdata; + count=SVAL(rparam,4); + + if (count > 0) { + printf("\n\nThis machine has a workgroup list:\n"); + printf("\n\tWorkgroup Master\n"); + printf("\t--------- -------\n"); + } + + for (i=0;i<count;i++) { + char *sname = p2; + int comment_offset = IVAL(p2,22) & 0xFFFF; + printf("\t%-16.16s %s\n", + sname, + comment_offset?rdata+comment_offset-converter:""); + + p2 += 26; + } + } + } + + if (rparam) free(rparam); + if (rdata) free(rdata); + + return(count>0); +} + + + + +void cmd_help(); + +/* This defines the commands supported by this client */ +struct +{ + char *name; + void (*fn)(); + char *description; +} commands[] = +{ + {"ls",cmd_dir,"<mask> list the contents of the current directory"}, + {"dir",cmd_dir,"<mask> list the contents of the current directory"}, + {"lcd",cmd_lcd,"[directory] change/report the local current working directory"}, + {"cd",cmd_cd,"[directory] change/report the remote directory"}, + {"pwd",cmd_pwd,"show current remote directory (same as 'cd' with no args)"}, + {"get",cmd_get,"<remote name> [local name] get a file"}, + {"mget",cmd_mget,"<mask> get all the matching files"}, + {"put",cmd_put,"<local name> [remote name] put a file"}, + {"mput",cmd_mput,"<mask> put all matching files"}, + {"rename",cmd_rename,"<src> <dest> rename some files"}, + {"more",cmd_more,"<remote name> view a remote file with your pager"}, + {"mask",cmd_select,"<mask> mask all filenames against this"}, + {"del",cmd_del,"<mask> delete all matching files"}, + {"rm",cmd_del,"<mask> delete all matching files"}, + {"mkdir",cmd_mkdir,"<directory> make a directory"}, + {"md",cmd_mkdir,"<directory> make a directory"}, + {"rmdir",cmd_rmdir,"<directory> remove a directory"}, + {"rd",cmd_rmdir,"<directory> remove a directory"}, + {"prompt",cmd_prompt,"toggle prompting for filenames for mget and mput"}, + {"recurse",cmd_recurse,"toggle directory recursion for mget and mput"}, + {"translate",cmd_translate,"toggle text translation for printing"}, + {"lowercase",cmd_lowercase,"toggle lowercasing of filenames for get"}, + {"print",cmd_print,"<file name> print a file"}, + {"printmode",cmd_printmode,"<graphics or text> set the print mode"}, + {"queue",cmd_queue,"show the print queue"}, + {"cancel",cmd_cancel,"<jobid> cancel a print queue entry"}, + {"stat",cmd_stat,"<file> get info on a file (experimental!)"}, + {"quit",send_logout,"logoff the server"}, + {"q",send_logout,"logoff the server"}, + {"exit",send_logout,"logoff the server"}, + {"newer",cmd_newer,"<file> only mget files newer than the specified local file"}, + {"archive",cmd_archive,"<level>\n0=ignore archive bit\n1=only get archive files\n2=only get archive files and reset archive bit\n3=get all files and reset archive bit"}, + {"tar",cmd_tar,"tar <c|x>[IXbgNa] current directory to/from <file name>" }, + {"blocksize",cmd_block,"blocksize <number> (default 20)" }, + {"tarmode",cmd_tarmode, + "<full|inc|reset|noreset> tar's behaviour towards archive bits" }, + {"setmode",cmd_setmode,"filename <setmode string> change modes of file"}, + {"help",cmd_help,"[command] give help on a command"}, + {"?",cmd_help,"[command] give help on a command"}, + {"!",NULL,"run a shell command on the local system"}, + {"",NULL,NULL} +}; + + +/******************************************************************* + lookup a command string in the list of commands, including + abbreviations + ******************************************************************/ +static int process_tok(fstring tok) +{ + int i = 0, matches = 0; + int cmd=0; + int tok_len = strlen(tok); + + while (commands[i].fn != NULL) + { + if (strequal(commands[i].name,tok)) + { + matches = 1; + cmd = i; + break; + } + else if (strnequal(commands[i].name, tok, tok_len+1)) + { + matches++; + cmd = i; + } + i++; + } + + if (matches == 0) + return(-1); + else if (matches == 1) + return(cmd); + else + return(-2); +} + +/**************************************************************************** +help +****************************************************************************/ +void cmd_help(void) +{ + int i=0,j; + fstring buf; + + if (next_token(NULL,buf,NULL)) + { + if ((i = process_tok(buf)) >= 0) + DEBUG(0,("HELP %s:\n\t%s\n\n",commands[i].name,commands[i].description)); + } + else + while (commands[i].description) + { + for (j=0; commands[i].description && (j<5); j++) { + DEBUG(0,("%-15s",commands[i].name)); + i++; + } + DEBUG(0,("\n")); + } +} + +/**************************************************************************** +open the client sockets +****************************************************************************/ +static BOOL open_sockets(int port ) +{ + static int last_port; + char *host; + pstring service2; + extern int Client; +#ifdef USENMB + BOOL failed = True; +#endif + + if (port == 0) port=last_port; + last_port=port; + + strupper(service); + + if (*desthost) + { + host = desthost; + } + else + { + strcpy(service2,service); + host = strtok(service2,"\\/"); + if (!host) { + DEBUG(0,("Badly formed host name\n")); + return(False); + } + strcpy(desthost,host); + } + + DEBUG(3,("Opening sockets\n")); + + if (*myname == 0) + { + get_myname(myname,NULL); + strupper(myname); + } + + if (!have_ip) + { + struct hostent *hp; + + if ((hp = Get_Hostbyname(host))) { + putip((char *)&dest_ip,(char *)hp->h_addr); + failed = False; + } else { +#ifdef USENMB + /* Try and resolve the name with the netbios server */ + int bcast; + pstring hs; + struct in_addr ip1, ip2; + + if ((bcast = open_socket_in(SOCK_DGRAM, 0, 3)) != -1) { + set_socket_options (bcast, "SO_BROADCAST"); + + if (!got_bcast && get_myname(hs, &ip1)) { + get_broadcast(&ip1, &bcast_ip, &ip2); + } + + if (name_query(bcast, host, 0x20, True, True, bcast_ip, &dest_ip,0)){ + failed = False; + } + close (bcast); + } +#endif + if (failed) { + DEBUG(0,("Get_Hostbyname: Unknown host %s.\n",host)); + return False; + } + } + } + + Client = open_socket_out(SOCK_STREAM, &dest_ip, port); + if (Client == -1) + return False; + + DEBUG(3,("Connected\n")); + + set_socket_options(Client,user_socket_options); + + return True; +} + +/**************************************************************************** +wait for keyboard activity, swallowing network packets +****************************************************************************/ +#ifdef CLIX +static char wait_keyboard(char *buffer) +#else +static void wait_keyboard(char *buffer) +#endif +{ + fd_set fds; + int selrtn; + struct timeval timeout; + +#ifdef CLIX + int delay = 0; +#endif + + while (1) + { + extern int Client; + FD_ZERO(&fds); + FD_SET(Client,&fds); +#ifndef CLIX + FD_SET(fileno(stdin),&fds); +#endif + + timeout.tv_sec = 20; + timeout.tv_usec = 0; +#ifdef CLIX + timeout.tv_sec = 0; +#endif + selrtn = sys_select(&fds,&timeout); + +#ifndef CLIX + if (FD_ISSET(fileno(stdin),&fds)) + return; +#else + { + char ch; + int f_flags; + int readret; + + f_flags = fcntl(fileno(stdin), F_GETFL, 0); + fcntl( fileno(stdin), F_SETFL, f_flags | O_NONBLOCK); + readret = read_data( fileno(stdin), &ch, 1); + fcntl(fileno(stdin), F_SETFL, f_flags); + if (readret == -1) + { + if (errno != EAGAIN) + { + /* should crash here */ + DEBUG(1,("readchar stdin failed\n")); + } + } + else if (readret != 0) + { + return ch; + } + } +#endif + if (FD_ISSET(Client,&fds)) + receive_smb(Client,buffer,0); + +#ifdef CLIX + delay++; + if (delay > 100000) + { + delay = 0; + chkpath("\\",False); + } +#else + chkpath("\\",False); +#endif + } +} + + +/**************************************************************************** +close and open the connection again +****************************************************************************/ +BOOL reopen_connection(char *inbuf,char *outbuf) +{ + static int open_count=0; + + open_count++; + + if (open_count>5) return(False); + + DEBUG(1,("Trying to re-open connection\n")); + + set_message(outbuf,0,0,True); + SCVAL(outbuf,smb_com,SMBtdis); + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,SHORT_TIMEOUT); + + close_sockets(); + if (!open_sockets(0)) return(False); + + return(send_login(inbuf,outbuf,True,True)); +} + +/**************************************************************************** + process commands from the client +****************************************************************************/ +BOOL process(char *base_directory) +{ + extern FILE *dbf; + pstring line; + + char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return(False); + + bzero(OutBuffer,smb_size); + + if (!send_login(InBuffer,OutBuffer,True,True)) + return(False); + + if (*base_directory) do_cd(base_directory); + + while (!feof(stdin)) + { + fstring tok; + int i; + + bzero(OutBuffer,smb_size); + + /* display a prompt */ + DEBUG(1,("smb: %s> ", CNV_LANG(cur_dir))); + fflush(dbf); + +#ifdef CLIX + line[0] = wait_keyboard(InBuffer); + /* this might not be such a good idea... */ + if ( line[0] == EOF) + break; +#else + wait_keyboard(InBuffer); +#endif + + /* and get a response */ +#ifdef CLIX + fgets( &line[1],999, stdin); +#else + if (!fgets(line,1000,stdin)) + break; +#endif + + /* input language code to internal one */ + CNV_INPUT (line); + + /* special case - first char is ! */ + if (*line == '!') + { + system(line + 1); + continue; + } + + /* and get the first part of the command */ + { + char *ptr = line; + if (!next_token(&ptr,tok,NULL)) continue; + } + + if ((i = process_tok(tok)) >= 0) + commands[i].fn(InBuffer,OutBuffer); + else if (i == -2) + DEBUG(0,("%s: command abbreviation ambiguous\n",CNV_LANG(tok))); + else + DEBUG(0,("%s: command not found\n",CNV_LANG(tok))); + } + + send_logout(); + return(True); +} + + +/**************************************************************************** +usage on the program +****************************************************************************/ +void usage(char *pname) +{ + DEBUG(0,("Usage: %s service <password> [-p port] [-d debuglevel] [-l log] ", + pname)); + +#ifdef KANJI + DEBUG(0,("[-t termcode] ")); +#endif /* KANJI */ + + DEBUG(0,("\nVersion %s\n",VERSION)); + DEBUG(0,("\t-p port listen on the specified port\n")); + DEBUG(0,("\t-d debuglevel set the debuglevel\n")); + DEBUG(0,("\t-l log basename. Basename for log/debug files\n")); + DEBUG(0,("\t-n netbios name. Use this name as my netbios name\n")); + DEBUG(0,("\t-N don't ask for a password\n")); + DEBUG(0,("\t-P connect to service as a printer\n")); + DEBUG(0,("\t-M host send a winpopup message to the host\n")); + DEBUG(0,("\t-m max protocol set the max protocol level\n")); + DEBUG(0,("\t-L host get a list of shares available on a host\n")); + DEBUG(0,("\t-I dest IP use this IP to connect to\n")); + DEBUG(0,("\t-E write messages to stderr instead of stdout\n")); + DEBUG(0,("\t-U username set the network username\n")); + DEBUG(0,("\t-W workgroup set the workgroup name\n")); +#ifdef KANJI + DEBUG(0,("\t-t terminal code terminal i/o code {sjis|euc|jis7|jis8|junet|hex}\n")); +#endif /* KANJI */ + DEBUG(0,("\t-T<c|x>IXgbNa command line tar\n")); + DEBUG(0,("\t-D directory start from directory\n")); + DEBUG(0,("\n")); +} + + + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc,char *argv[]) +{ + fstring base_directory; + char *pname = argv[0]; + int port = 139; + int opt; + extern FILE *dbf; + extern char *optarg; + extern int optind; + pstring query_host; + BOOL message = False; + extern char tar_type; + + *query_host = 0; + *base_directory = 0; + + DEBUGLEVEL = 2; + + setup_logging(pname,True); + + TimeInit(); + charset_initialise(); + + pid = getpid(); + uid = getuid(); + gid = getgid(); + mid = pid + 100; + myumask = umask(0); + umask(myumask); + + if (getenv("USER")) + { + strcpy(username,getenv("USER")); + strupper(username); + } + + if (*username == 0 && getenv("LOGNAME")) + { + strcpy(username,getenv("LOGNAME")); + strupper(username); + } + + if (argc < 2) + { + usage(pname); + exit(1); + } + + if (*argv[1] != '-') + { + + strcpy(service,argv[1]); + argc--; + argv++; + + if (count_chars(service,'\\') < 3) + { + usage(pname); + printf("\n%s: Not enough '\\' characters in service\n",service); + exit(1); + } + +/* + if (count_chars(service,'\\') > 3) + { + usage(pname); + printf("\n%s: Too many '\\' characters in service\n",service); + exit(1); + } + */ + + if (argc > 1 && (*argv[1] != '-')) + { + got_pass = True; + strcpy(password,argv[1]); + memset(argv[1],'X',strlen(argv[1])); + argc--; + argv++; + } + } + +#ifdef KANJI + setup_term_code (KANJI); + while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:t:m:W:T:D:")) != EOF) +#else + while ((opt = getopt (argc, argv, "B:O:M:i:Nn:d:Pp:l:hI:EB:U:L:m:W:T:D:")) != EOF) +#endif /* KANJI */ + switch (opt) + { + case 'm': + max_protocol = interpret_protocol(optarg,max_protocol); + break; + case 'O': + strcpy(user_socket_options,optarg); + break; + case 'M': + name_type = 3; + strcpy(desthost,optarg); + strupper(desthost); + message = True; + break; + case 'B': + bcast_ip = *interpret_addr2(optarg); + got_bcast = True; + break; + case 'D': + strcpy(base_directory,optarg); + break; + case 'T': + if (!tar_parseargs(argc, argv, optarg, optind)) { + usage(pname); + exit(1); + } + break; + case 'i': + strcpy(scope,optarg); + break; + case 'L': + got_pass = True; + strcpy(query_host,optarg); + break; + case 'U': + { + char *p; + strcpy(username,optarg); + if ((p=strchr(username,'%'))) + { + *p = 0; + strcpy(password,p+1); + got_pass = True; + memset(strchr(optarg,'%')+1,'X',strlen(password)); + } + } + + break; + case 'W': + strcpy(workgroup,optarg); + break; + case 'E': + dbf = stderr; + break; + case 'I': + { + dest_ip = *interpret_addr2(optarg); + if (zero_ip(dest_ip)) exit(1); + have_ip = True; + } + break; + case 'n': + strcpy(myname,optarg); + break; + case 'N': + got_pass = True; + break; + case 'P': + connect_as_printer = True; + break; + case 'd': + if (*optarg == 'A') + DEBUGLEVEL = 10000; + else + DEBUGLEVEL = atoi(optarg); + break; + case 'l': + sprintf(debugf,"%s.client",optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 'h': + usage(pname); + exit(0); + break; +#ifdef KANJI + case 't': + if (!setup_term_code (optarg)) { + DEBUG(0, ("%s: unknown terminal code name\n", optarg)); + usage (pname); + exit (1); + } + break; +#endif /* KANJI */ + default: + usage(pname); + exit(1); + } + + if (!tar_type && !*query_host && !*service && !message) + { + usage(pname); + exit(1); + } + + + DEBUG(3,("%s client started (version %s)\n",timestring(),VERSION)); + + get_myname(*myname?NULL:myname,&myip); + strupper(myname); + + if (tar_type) { + recurse=True; + + if (open_sockets(port)) { + char *InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + char *OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + int ret; + + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return(1); + + bzero(OutBuffer,smb_size); + if (!send_login(InBuffer,OutBuffer,True,True)) + return(False); + + if (*base_directory) do_cd(base_directory); + + ret=process_tar(InBuffer, OutBuffer); + + send_logout(); + close_sockets(); + return(ret); + } else + return(1); + } + + if (*query_host) + { + int ret = 0; + sprintf(service,"\\\\%s\\IPC$",query_host); + strupper(service); + connect_as_ipc = True; + if (open_sockets(port)) + { +#if 0 + *username = 0; +#endif + if (!send_login(NULL,NULL,True,True)) + return(1); + + server_info(); + if (!browse_host(True)) { + sleep(1); + browse_host(True); + } + if (!list_servers()) { + sleep(1); + list_servers(); + } + + send_logout(); + close_sockets(); + } + + return(ret); + } + + if (message) + { + int ret = 0; + if (open_sockets(port)) + { + pstring inbuf,outbuf; + bzero(outbuf,smb_size); + if (!send_session_request(inbuf,outbuf)) + return(1); + + send_message(inbuf,outbuf); + + close_sockets(); + } + + return(ret); + } + + if (open_sockets(port)) + { + if (!process(base_directory)) + { + close_sockets(); + return(1); + } + close_sockets(); + } + else + return(1); + + return(0); +} + + +/* error code stuff - put together by Merik Karman + merik@blackadder.dsh.oz.au */ + +typedef struct +{ + char *name; + int code; + char *message; +} err_code_struct; + +/* Dos Error Messages */ +err_code_struct dos_msgs[] = { + {"ERRbadfunc",1,"Invalid function."}, + {"ERRbadfile",2,"File not found."}, + {"ERRbadpath",3,"Directory invalid."}, + {"ERRnofids",4,"No file descriptors available"}, + {"ERRnoaccess",5,"Access denied."}, + {"ERRbadfid",6,"Invalid file handle."}, + {"ERRbadmcb",7,"Memory control blocks destroyed."}, + {"ERRnomem",8,"Insufficient server memory to perform the requested function."}, + {"ERRbadmem",9,"Invalid memory block address."}, + {"ERRbadenv",10,"Invalid environment."}, + {"ERRbadformat",11,"Invalid format."}, + {"ERRbadaccess",12,"Invalid open mode."}, + {"ERRbaddata",13,"Invalid data."}, + {"ERR",14,"reserved."}, + {"ERRbaddrive",15,"Invalid drive specified."}, + {"ERRremcd",16,"A Delete Directory request attempted to remove the server's current directory."}, + {"ERRdiffdevice",17,"Not same device."}, + {"ERRnofiles",18,"A File Search command can find no more files matching the specified criteria."}, + {"ERRbadshare",32,"The sharing mode specified for an Open conflicts with existing FIDs on the file."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRfilexists",80,"The file named in a Create Directory, Make New File or Link request already exists."}, + {"ERRbadpipe",230,"Pipe invalid."}, + {"ERRpipebusy",231,"All instances of the requested pipe are busy."}, + {"ERRpipeclosing",232,"Pipe close in progress."}, + {"ERRnotconnected",233,"No process on other end of pipe."}, + {"ERRmoredata",234,"There is more data to be returned."}, + {"ERRinvgroup",2455,"Invalid workgroup (try the -W option)"}, + {NULL,-1,NULL}}; + +/* Server Error Messages */ +err_code_struct server_msgs[] = { + {"ERRerror",1,"Non-specific error code."}, + {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."}, + {"ERRbadtype",3,"reserved."}, + {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."}, + {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."}, + {"ERRinvnetname",6,"Invalid network name in tree connect."}, + {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."}, + {"ERRqfull",49,"Print queue full (files) -- returned by open print file."}, + {"ERRqtoobig",50,"Print queue full -- no space."}, + {"ERRqeof",51,"EOF on print queue dump."}, + {"ERRinvpfid",52,"Invalid print file FID."}, + {"ERRsmbcmd",64,"The server did not recognize the command received."}, + {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."}, + {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."}, + {"ERRreserved",68,"reserved."}, + {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."}, + {"ERRreserved",70,"reserved."}, + {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."}, + {"ERRpaused",81,"Server is paused."}, + {"ERRmsgoff",82,"Not receiving messages."}, + {"ERRnoroom",83,"No room to buffer message."}, + {"ERRrmuns",87,"Too many remote user names."}, + {"ERRtimeout",88,"Operation timed out."}, + {"ERRnoresource",89,"No resources currently available for request."}, + {"ERRtoomanyuids",90,"Too many UIDs active on this session."}, + {"ERRbaduid",91,"The UID is not known as a valid ID on this session."}, + {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."}, + {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."}, + {"ERRcontmpx",252,"Continue in MPX mode."}, + {"ERRreserved",253,"reserved."}, + {"ERRreserved",254,"reserved."}, + {"ERRnosupport",0xFFFF,"Function not supported."}, + {NULL,-1,NULL}}; + +/* Hard Error Messages */ +err_code_struct hard_msgs[] = { + {"ERRnowrite",19,"Attempt to write on write-protected diskette."}, + {"ERRbadunit",20,"Unknown unit."}, + {"ERRnotready",21,"Drive not ready."}, + {"ERRbadcmd",22,"Unknown command."}, + {"ERRdata",23,"Data error (CRC)."}, + {"ERRbadreq",24,"Bad request structure length."}, + {"ERRseek",25 ,"Seek error."}, + {"ERRbadmedia",26,"Unknown media type."}, + {"ERRbadsector",27,"Sector not found."}, + {"ERRnopaper",28,"Printer out of paper."}, + {"ERRwrite",29,"Write fault."}, + {"ERRread",30,"Read fault."}, + {"ERRgeneral",31,"General failure."}, + {"ERRbadshare",32,"A open conflicts with an existing open."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRwrongdisk",34,"The wrong disk was found in a drive."}, + {"ERRFCBUnavail",35,"No FCBs are available to process request."}, + {"ERRsharebufexc",36,"A sharing buffer has been exceeded."}, + {NULL,-1,NULL}}; + + +struct +{ + int code; + char *class; + err_code_struct *err_msgs; +} err_classes[] = { + {0,"SUCCESS",NULL}, + {0x01,"ERRDOS",dos_msgs}, + {0x02,"ERRSRV",server_msgs}, + {0x03,"ERRHRD",hard_msgs}, + {0x04,"ERRXOS",NULL}, + {0xE1,"ERRRMX1",NULL}, + {0xE2,"ERRRMX2",NULL}, + {0xE3,"ERRRMX3",NULL}, + {0xFF,"ERRCMD",NULL}, + {-1,NULL,NULL}}; + + +/**************************************************************************** +return a SMB error string from a SMB buffer +****************************************************************************/ +char *smb_errstr(char *inbuf) +{ + static pstring ret; + int class = CVAL(inbuf,smb_rcls); + int num = SVAL(inbuf,smb_err); + int i,j; + + for (i=0;err_classes[i].class;i++) + if (err_classes[i].code == class) + { + if (err_classes[i].err_msgs) + { + err_code_struct *err = err_classes[i].err_msgs; + for (j=0;err[j].name;j++) + if (num == err[j].code) + { + if (DEBUGLEVEL > 0) + sprintf(ret,"%s - %s (%s)",err_classes[i].class, + err[j].name,err[j].message); + else + sprintf(ret,"%s - %s",err_classes[i].class,err[j].name); + return ret; + } + } + + sprintf(ret,"%s - %d",err_classes[i].class,num); + return ret; + } + + sprintf(ret,"ERROR: Unknown error (%d,%d)",class,num); + return(ret); +} diff --git a/source3/client/clitar.c b/source3/client/clitar.c new file mode 100644 index 0000000000..1433ec5941 --- /dev/null +++ b/source3/client/clitar.c @@ -0,0 +1,1713 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Tar Extensions + Copyright (C) Ricky Poulten 1995 + + 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. +*/ + + +#include "includes.h" +#include "clitar.h" + +extern void setup_pkt(char *outbuf); +extern BOOL reopen_connection(char *inbuf,char *outbuf); +extern void do_dir(char *inbuf,char *outbuf,char *Mask,int attribute,void (*fn)(),BOOL recurse_dir); + +int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind); + +extern BOOL recurse; + +#define SEPARATORS " \t\n\r" +extern int DEBUGLEVEL; +extern int Client; + +/* These defines are for the do_setrattr routine, to indicate + * setting and reseting of file attributes in the function call */ +#define ATTRSET 1 +#define ATTRRESET 0 + +static int attribute = aDIR | aSYSTEM | aHIDDEN; + +#ifndef CLIENT_TIMEOUT +#define CLIENT_TIMEOUT (30*1000) +#endif + +static char *tarbuf; +static int tp, ntarf, tbufsiz; +/* Incremental mode */ +BOOL tar_inc=False; +/* Reset archive bit */ +BOOL tar_reset=False; +/* Include / exclude mode (true=include, false=exclude) */ +BOOL tar_excl=True; +char tar_type='\0'; +static char **cliplist=NULL; +static int clipn=0; + +extern file_info def_finfo; +extern BOOL lowercase; +extern int cnum; +extern BOOL readbraw_supported; +extern int max_xmit; +extern pstring cur_dir; +extern int get_total_time_ms; +extern int get_total_size; +extern int Protocol; + +int blocksize=20; +int tarhandle; + +static void writetarheader(); +static void do_atar(); +static void do_tar(); +static void oct_it(); +static void fixtarname(); +static int dotarbuf(); +static void dozerobuf(); +static void dotareof(); +static void initarbuf(); +static int do_setrattr(); +void cmd_tar(); +int process_tar(); +char **toktocliplist(); +int clipfind(); +/* restore functions */ +static long readtarheader(); +static long unoct(); +static void do_tarput(); +static void unfixtarname(); + +/* + * tar specific utitlities + */ + +/**************************************************************************** +Write a tar header to buffer +****************************************************************************/ +static void writetarheader(int f, char *aname, int size, time_t mtime, + char *amode) +{ + union hblock hb; + int i, chk, l; + char *jp; + + memset(hb.dummy, 0, sizeof(hb.dummy)); + + l=strlen(aname); + if (l >= NAMSIZ) + { + DEBUG(0, ("tar file %s name length exceeds NAMSIZ\n", aname)); + } + + /* use l + 1 to do the null too */ + fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1); + + if (lowercase) + strlower(hb.dbuf.name); + + /* write out a "standard" tar format header */ + + hb.dbuf.name[NAMSIZ-1]='\0'; + strcpy(hb.dbuf.mode, amode); + oct_it(0L, 8, hb.dbuf.uid); + oct_it(0L, 8, hb.dbuf.gid); + oct_it((long) size, 13, hb.dbuf.size); + oct_it((long) mtime, 13, hb.dbuf.mtime); + memcpy(hb.dbuf.chksum, " ", sizeof(hb.dbuf.chksum)); + hb.dbuf.linkflag='0'; + memset(hb.dbuf.linkname, 0, NAMSIZ); + + for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++); + + oct_it((long) chk, 8, hb.dbuf.chksum); + hb.dbuf.chksum[6] = '\0'; + + (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy)); +} + +/**************************************************************************** +Read a tar header into a hblock structure, and validate +***************************************************************************/ +static long readtarheader(union hblock *hb, file_info *finfo, char *prefix) +{ + long chk, fchk; + int i; + char *jp; + + /* + * read in a "standard" tar format header - we're not that interested + * in that many fields, though + */ + + /* check the checksum */ + for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) chk+=(0xFF & *jp++); + + if (chk == 0) + return chk; + + /* compensate for blanks in chksum header */ + for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;) + chk-=(0xFF & *jp++); + + chk += ' ' * sizeof(hb->dbuf.chksum); + + fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum)); + + DEBUG(5, ("checksum totals chk=%d fchk=%d chksum=%s\n", + chk, fchk, hb->dbuf.chksum)); + + if (fchk != chk) + { + DEBUG(0, ("checksums don't match %d %d\n", fchk, chk)); + return -1; + } + + strcpy(finfo->name, prefix); + + /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */ + unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name, + strlen(hb->dbuf.name) + 1); + +/* can't handle links at present */ + if (hb->dbuf.linkflag != '0') { + if (hb->dbuf.linkflag == 0) { + DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n", + finfo->name)); + } else { + DEBUG(0, ("this tar file appears to contain some kind of link - ignoring\n")); + return -2; + } + } + + if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR) + || (*(finfo->name+strlen(finfo->name)-1) == '\\')) + { + finfo->mode=aDIR; + } + else + finfo->mode=0; /* we don't care about mode at the moment, we'll + * just make it a regular file */ + /* + * Bug fix by richard@sj.co.uk + * + * REC: restore times correctly (as does tar) + * We only get the modification time of the file; set the creation time + * from the mod. time, and the access time to current time + */ + finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8); + finfo->atime = time(NULL); + finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size)); + + return True; +} + +/**************************************************************************** +Write out the tar buffer to tape or wherever +****************************************************************************/ +static int dotarbuf(int f, char *b, int n) +{ + int fail=1, writ=n; + + /* This routine and the next one should be the only ones that do write()s */ + if (tp + n >= tbufsiz) + { + int diff; + + diff=tbufsiz-tp; + memcpy(tarbuf + tp, b, diff); + fail=fail && (1+write(f, tarbuf, tbufsiz)); + n-=diff; + b+=diff; + tp=0; + + while (n >= tbufsiz) + { + fail=fail && (1 + write(f, b, tbufsiz)); + n-=tbufsiz; + b+=tbufsiz; + } + } + if (n>0) { + memcpy(tarbuf+tp, b, n); + tp+=n; + } + + return(fail ? writ : 0); +} + +/**************************************************************************** +Write a zeros to buffer / tape +****************************************************************************/ +static void dozerobuf(int f, int n) +{ + /* short routine just to write out n zeros to buffer - + * used to round files to nearest block + * and to do tar EOFs */ + + if (n+tp >= tbufsiz) + { + memset(tarbuf+tp, 0, tbufsiz-tp); + write(f, tarbuf, tbufsiz); + memset(tarbuf, 0, (tp+=n-tbufsiz)); + } + else + { + memset(tarbuf+tp, 0, n); + tp+=n; + } +} + +/**************************************************************************** +Malloc tape buffer +****************************************************************************/ +static void initarbuf() +{ + /* initialize tar buffer */ + tbufsiz=blocksize*TBLOCK; + tarbuf=malloc(tbufsiz); + + /* reset tar buffer pointer and tar file counter */ + tp=0; ntarf=0; +} + +/**************************************************************************** +Write two zero blocks at end of file +****************************************************************************/ +static void dotareof(int f) +{ + struct stat stbuf; + /* Two zero blocks at end of file, write out full buffer */ + + (void) dozerobuf(f, TBLOCK); + (void) dozerobuf(f, TBLOCK); + + if (fstat(f, &stbuf) == -1) + { + DEBUG(0, ("Couldn't stat file handle\n")); + return; + } + + /* Could be a pipe, in which case S_ISREG should fail, + * and we should write out at full size */ + if (tp > 0) write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz); +} + +/**************************************************************************** +(Un)mangle DOS pathname, make nonabsolute +****************************************************************************/ +static void fixtarname(char *tptr, char *fp, int l) +{ + /* add a '.' to start of file name, convert from ugly dos \'s in path + * to lovely unix /'s :-} */ + + *tptr++='.'; +#ifdef KANJI + while (l > 0) { + if (is_shift_jis (*fp)) { + *tptr++ = *fp++; + *tptr++ = *fp++; + l -= 2; + } else if (is_kana (*fp)) { + *tptr++ = *fp++; + l--; + } else if (*fp == '\\') { + *tptr++ = '/'; + fp++; + l--; + } else { + *tptr++ = *fp++; + l--; + } + } +#else + while (l--) { *tptr=(*fp == '\\') ? '/' : *fp; tptr++; fp++; } +#endif +} + +/**************************************************************************** +Convert from decimal to octal string +****************************************************************************/ +static void oct_it (register long value, register int ndgs, register char *p) +{ + /* Converts long to octal string, pads with leading zeros */ + + /* skip final null, but do final space */ + --ndgs; + p[--ndgs] = ' '; + + /* Loop does at least one digit */ + do { + p[--ndgs] = '0' + (char) (value & 7); + value >>= 3; + } + while (ndgs > 0 && value != 0); + + /* Do leading zeros */ + while (ndgs > 0) + p[--ndgs] = '0'; +} + +/**************************************************************************** +Convert from octal string to long +***************************************************************************/ +static long unoct(char *p, int ndgs) +{ + long value=0; + /* Converts octal string to long, ignoring any non-digit */ + + while (--ndgs) + { + if (isdigit(*p)) + value = (value << 3) | (long) (*p - '0'); + + p++; + } + + return value; +} + +/**************************************************************************** +Compare two strings in a slash insensitive way +***************************************************************************/ +int strslashcmp(const char *s1, const char *s2) +{ + while(*s1 && *s2 && + (*s1 == *s2 + || tolower(*s1) == tolower(*s2) + || (*s1 == '\\' && *s2=='/') + || (*s1 == '/' && *s2=='\\'))) { + s1++; s2++; + } + + return *s1-*s2; +} + +/* + * general smb utility functions + */ +/**************************************************************************** +Set DOS file attributes +***************************************************************************/ +static int do_setrattr(char *fname, int attr, int setit) +{ + /* + * First get the existing attribs from existing file + */ + char *inbuf,*outbuf; + char *p; + pstring name; + int fattr; + + strcpy(name,fname); + strcpy(fname,"\\"); + strcat(fname,name); + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return False; + } + + /* send an smb getatr message */ + + memset(outbuf,0,smb_size); + set_message(outbuf,0,2 + strlen(fname),True); + CVAL(outbuf,smb_com) = SMBgetatr; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,fname); + p += (strlen(fname)+1); + + *p++ = 4; + *p++ = 0; + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + DEBUG(5,("getatr: %s\n",smb_errstr(inbuf))); + else + { + DEBUG(5,("\nattr 0x%X time %d size %d\n", + (int)CVAL(inbuf,smb_vwv0), + SVAL(inbuf,smb_vwv1), + SVAL(inbuf,smb_vwv3))); + } + + fattr=CVAL(inbuf,smb_vwv0); + + /* combine found attributes with bits to be set or reset */ + + attr=setit ? (fattr | attr) : (fattr & ~attr); + + /* now try and set attributes by sending smb reset message */ + + /* clear out buffer and start again */ + memset(outbuf,0,smb_size); + set_message(outbuf,8,4 + strlen(fname),True); + CVAL(outbuf,smb_com) = SMBsetatr; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,attr); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,fname); + p += (strlen(fname)+1); + + *p++ = 4; + *p++ = 0; + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s setting attributes on file %s\n", + smb_errstr(inbuf), fname)); + free(inbuf);free(outbuf); + return(False); + } + + free(inbuf);free(outbuf); + return(True); +} + +/**************************************************************************** +Create a file on a share +***************************************************************************/ +static BOOL smbcreat(file_info finfo, int *fnum, char *inbuf, char *outbuf) +{ + char *p; + /* *must* be called with buffer ready malloc'ed */ + /* open remote file */ + + memset(outbuf,0,smb_size); + set_message(outbuf,3,2 + strlen(finfo.name),True); + CVAL(outbuf,smb_com) = SMBcreate; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,finfo.mode); + put_dos_date3(outbuf,smb_vwv1,finfo.mtime); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,finfo.name); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf), + finfo.name)); + return 0; + } + + *fnum = SVAL(inbuf,smb_vwv0); + return True; +} + +/**************************************************************************** +Write a file to a share +***************************************************************************/ +static BOOL smbwrite(int fnum, int n, int low, int high, int left, + char *bufferp, char *inbuf, char *outbuf) +{ + /* *must* be called with buffer ready malloc'ed */ + + memset(outbuf,0,smb_size); + set_message(outbuf,5,n + 3,True); + + memcpy(smb_buf(outbuf)+3, bufferp, n); + + set_message(outbuf,5,n + 3, False); + CVAL(outbuf,smb_com) = SMBwrite; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,n); + SIVAL(outbuf,smb_vwv2,low); + SSVAL(outbuf,smb_vwv4,left); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,n); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s writing remote file\n",smb_errstr(inbuf))); + return False; + } + + if (n != SVAL(inbuf,smb_vwv0)) + { + DEBUG(0,("Error: only wrote %d bytes out of %d\n", + SVAL(inbuf,smb_vwv0), n)); + return False; + } + + return True; +} + +/**************************************************************************** +Close a file on a share +***************************************************************************/ +static BOOL smbshut(file_info finfo, int fnum, char *inbuf, char *outbuf) +{ + /* *must* be called with buffer ready malloc'ed */ + + memset(outbuf,0,smb_size); + set_message(outbuf,3,0,True); + CVAL(outbuf,smb_com) = SMBclose; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + put_dos_date3(outbuf,smb_vwv1,finfo.mtime); + + DEBUG(3,("Setting date to %s (0x%X)", + asctime(LocalTime(&finfo.mtime,GMT_TO_LOCAL)), + finfo.mtime)); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s closing remote file %s\n",smb_errstr(inbuf), + finfo.name)); + return False; + } + + return True; +} + +/**************************************************************************** +Verify existence of path on share +***************************************************************************/ +static BOOL smbchkpath(char *fname, char *inbuf, char *outbuf) +{ + char *p; + + memset(outbuf,0,smb_size); + set_message(outbuf,0,4 + strlen(fname),True); + CVAL(outbuf,smb_com) = SMBchkpth; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,fname); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + DEBUG(5,("smbchkpath: %s\n",smb_errstr(inbuf))); + + return(CVAL(inbuf,smb_rcls) == 0); +} + +/**************************************************************************** +Make a directory on share +***************************************************************************/ +static BOOL smbmkdir(char *fname, char *inbuf, char *outbuf) +{ + /* *must* be called with buffer ready malloc'ed */ + char *p; + + memset(outbuf,0,smb_size); + set_message(outbuf,0,2 + strlen(fname),True); + + CVAL(outbuf,smb_com) = SMBmkdir; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + p = smb_buf(outbuf); + *p++ = 4; + strcpy(p,fname); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("%s making remote directory %s\n", + smb_errstr(inbuf),fname)); + return(False); + } + + return(True); +} + +/**************************************************************************** +Ensure a remote path exists (make if necessary) +***************************************************************************/ +static BOOL ensurepath(char *fname, char *inbuf, char *outbuf) +{ + /* *must* be called with buffer ready malloc'ed */ + /* ensures path exists */ + + pstring partpath, ffname; + char *p=fname, *basehack; + + *partpath = 0; + + /* fname copied to ffname so can strtok */ + + strcpy(ffname, fname); + + /* do a `basename' on ffname, so don't try and make file name directory */ + if ((basehack=strrchr(ffname, '\\')) == NULL) + return True; + else + *basehack='\0'; + + p=strtok(ffname, "\\"); + + while (p) + { + strcat(partpath, p); + + if (!smbchkpath(partpath, inbuf, outbuf)) { + if (!smbmkdir(partpath, inbuf, outbuf)) + { + DEBUG(0, ("Error mkdirhiering\n")); + return False; + } + else + DEBUG(3, ("mkdirhiering %s\n", partpath)); + + } + + strcat(partpath, "\\"); + p = strtok(NULL,"/\\"); + } + + return True; +} + +/* + * smbclient functions + */ +/**************************************************************************** +append one remote file to the tar file +***************************************************************************/ +static void do_atar(char *rname,char *lname,file_info *finfo1) +{ + int fnum; + uint32 nread=0; + char *p; + char *inbuf,*outbuf; + file_info finfo; + BOOL close_done = False; + BOOL shallitime=True; + BOOL ignore_close_error = False; + char *dataptr=NULL; + int datalen=0; + + struct timeval tp_start; + GetTimeOfDay(&tp_start); + + if (finfo1) + finfo = *finfo1; + else + finfo = def_finfo; + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + memset(outbuf,0,smb_size); + set_message(outbuf,15,1 + strlen(rname),True); + + CVAL(outbuf,smb_com) = SMBopenX; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv2,1); + SSVAL(outbuf,smb_vwv3,(DENY_NONE<<4)); + SSVAL(outbuf,smb_vwv4,aSYSTEM | aHIDDEN); + SSVAL(outbuf,smb_vwv5,aSYSTEM | aHIDDEN); + SSVAL(outbuf,smb_vwv8,1); + + p = smb_buf(outbuf); + strcpy(p,rname); + p = skip_string(p,1); + + dos_clean_name(rname); + + /* do a chained openX with a readX? */ + if (finfo.size > 0) + { + SSVAL(outbuf,smb_vwv0,SMBreadX); + SSVAL(outbuf,smb_vwv1,PTR_DIFF(p,outbuf) - 4); + memset(p,0,200); + p -= smb_wct; + SSVAL(p,smb_wct,10); + SSVAL(p,smb_vwv0,0xFF); + SSVAL(p,smb_vwv5,MIN(max_xmit-500,finfo.size)); + SSVAL(p,smb_vwv9,MIN(0xFFFF,finfo.size)); + smb_setlen(outbuf,smb_len(outbuf)+11*2+1); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + if (CVAL(inbuf,smb_rcls) == ERRSRV && + SVAL(inbuf,smb_err) == ERRnoresource && + reopen_connection(inbuf,outbuf)) + { + do_atar(rname,lname,finfo1); + free(inbuf);free(outbuf); + return; + } + + DEBUG(0,("%s opening remote file %s\n",smb_errstr(inbuf),rname)); + free(inbuf);free(outbuf); + return; + } + + strcpy(finfo.name,rname); + if (!finfo1) + { + finfo.mode = SVAL(inbuf,smb_vwv3); + finfo.size = IVAL(inbuf,smb_vwv4); + finfo.mtime = make_unix_date3(inbuf+smb_vwv6); + finfo.atime = finfo.ctime = finfo.mtime; + } + + DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode)); + + fnum = SVAL(inbuf,smb_vwv2); + + if (tar_inc && !(finfo.mode & aARCH)) + { + DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name)); + shallitime=0; + } + else + { + if (SVAL(inbuf,smb_vwv0) == SMBreadX) + { + p = (inbuf+4+SVAL(inbuf,smb_vwv1)) - smb_wct; + datalen = SVAL(p,smb_vwv5); + dataptr = inbuf + 4 + SVAL(p,smb_vwv6); + } + else + { + dataptr = NULL; + datalen = 0; + } + + DEBUG(2,("getting file %s of size %d bytes as a tar file %s", + finfo.name, + finfo.size, + lname)); + + /* write a tar header, don't bother with mode - just set to 100644 */ + writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0"); + + while (nread < finfo.size && !close_done) + { + int method = -1; + static BOOL can_chain_close=True; + + p=NULL; + + DEBUG(3,("nread=%d\n",nread)); + + /* 3 possible read types. readbraw if a large block is required. + readX + close if not much left and read if neither is supported */ + + /* we might have already read some data from a chained readX */ + if (dataptr && datalen>0) + method=3; + + /* if we can finish now then readX+close */ + if (method<0 && can_chain_close && (Protocol >= PROTOCOL_LANMAN1) && + ((finfo.size - nread) < + (max_xmit - (2*smb_size + 13*SIZEOFWORD + 300)))) + method = 0; + + /* if we support readraw then use that */ + if (method<0 && readbraw_supported) + method = 1; + + /* if we can then use readX */ + if (method<0 && (Protocol >= PROTOCOL_LANMAN1)) + method = 2; + + + switch (method) + { + /* use readX */ + case 0: + case 2: + if (method == 0) + close_done = True; + + /* use readX + close */ + memset(outbuf,0,smb_size); + set_message(outbuf,10,0,True); + CVAL(outbuf,smb_com) = SMBreadX; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + if (close_done) + { + CVAL(outbuf,smb_vwv0) = SMBclose; + SSVAL(outbuf,smb_vwv1,PTR_DIFF(smb_buf(outbuf),outbuf) - 4); + } + else + CVAL(outbuf,smb_vwv0) = 0xFF; + + + SSVAL(outbuf,smb_vwv2,fnum); + SIVAL(outbuf,smb_vwv3,nread); + SSVAL(outbuf,smb_vwv5,MIN(max_xmit-200,finfo.size - nread)); + SSVAL(outbuf,smb_vwv6,0); + SIVAL(outbuf,smb_vwv7,0); + SSVAL(outbuf,smb_vwv9,MIN(0xFFFF,finfo.size-nread)); + + if (close_done) + { + p = smb_buf(outbuf); + memset(p,0,9); + + CVAL(p,0) = 3; + SSVAL(p,1,fnum); + SIVALS(p,3,-1); + + /* now set the total packet length */ + smb_setlen(outbuf,smb_len(outbuf)+9); + } + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf))); + break; + } + + if (close_done && + SVAL(inbuf,smb_vwv0) != SMBclose) + { + /* NOTE: WfWg sometimes just ignores the chained + command! This seems to break the spec? */ + DEBUG(3,("Rejected chained close?\n")); + close_done = False; + can_chain_close = False; + ignore_close_error = True; + } + + datalen = SVAL(inbuf,smb_vwv5); + dataptr = inbuf + 4 + SVAL(inbuf,smb_vwv6); + break; + + + /* use readbraw */ + case 1: + { + static int readbraw_size = 0xFFFF; + + extern int Client; + memset(outbuf,0,smb_size); + set_message(outbuf,8,0,True); + CVAL(outbuf,smb_com) = SMBreadbraw; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + SSVAL(outbuf,smb_vwv0,fnum); + SIVAL(outbuf,smb_vwv1,nread); + SSVAL(outbuf,smb_vwv3,MIN(finfo.size-nread,readbraw_size)); + SSVAL(outbuf,smb_vwv4,0); + SIVALS(outbuf,smb_vwv5,-1); + send_smb(Client,outbuf); + + /* Now read the raw data into the buffer and write it */ + if(read_smb_length(Client,inbuf,0) == -1) { + DEBUG(0,("Failed to read length in readbraw\n")); + exit(1); + } + + /* Even though this is not an smb message, smb_len + returns the generic length of an smb message */ + datalen = smb_len(inbuf); + + if (datalen == 0) + { + /* we got a readbraw error */ + DEBUG(4,("readbraw error - reducing size\n")); + readbraw_size = (readbraw_size * 9) / 10; + + if (readbraw_size < max_xmit) + { + DEBUG(0,("disabling readbraw\n")); + readbraw_supported = False; + } + + dataptr=NULL; + continue; + } + + if(read_data(Client,inbuf,datalen) != datalen) { + DEBUG(0,("Failed to read data in readbraw\n")); + exit(1); + } + dataptr = inbuf; + } + break; + + case 3: + /* we've already read some data with a chained readX */ + break; + + default: + /* use plain read */ + memset(outbuf,0,smb_size); + set_message(outbuf,5,0,True); + CVAL(outbuf,smb_com) = SMBread; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,MIN(max_xmit-200,finfo.size - nread)); + SIVAL(outbuf,smb_vwv2,nread); + SSVAL(outbuf,smb_vwv4,finfo.size - nread); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s reading remote file\n",smb_errstr(inbuf))); + break; + } + + datalen = SVAL(inbuf,smb_vwv0); + dataptr = smb_buf(inbuf) + 3; + break; + } + + + /* add received bits of file to buffer - dotarbuf will + * write out in 512 byte intervals */ + if (dotarbuf(tarhandle,dataptr,datalen) != datalen) + { + DEBUG(0,("Error writing local file\n")); + break; + } + + nread += datalen; + if (datalen == 0) + { + DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname)); + break; + } + + dataptr=NULL; + datalen=0; + } + + /* round tar file to nearest block */ + if (finfo.size % TBLOCK) + dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK)); + + ntarf++; + } + + if (!close_done) + { + memset(outbuf,0,smb_size); + set_message(outbuf,3,0,True); + CVAL(outbuf,smb_com) = SMBclose; + SSVAL(outbuf,smb_tid,cnum); + setup_pkt(outbuf); + + SSVAL(outbuf,smb_vwv0,fnum); + SIVALS(outbuf,smb_vwv1,-1); + + send_smb(Client,outbuf); + receive_smb(Client,inbuf,CLIENT_TIMEOUT); + + if (!ignore_close_error && CVAL(inbuf,smb_rcls) != 0) + { + DEBUG(0,("Error %s closing remote file\n",smb_errstr(inbuf))); + free(inbuf);free(outbuf); + return; + } + } + + if (shallitime) + { + struct timeval tp_end; + int this_time; + + /* if shallitime is true then we didn't skip */ + if (tar_reset) (void) do_setrattr(finfo.name, aARCH, ATTRRESET); + + GetTimeOfDay(&tp_end); + this_time = + (tp_end.tv_sec - tp_start.tv_sec)*1000 + + (tp_end.tv_usec - tp_start.tv_usec)/1000; + get_total_time_ms += this_time; + get_total_size += finfo.size; + + /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */ + DEBUG(2,("(%g kb/s) (average %g kb/s)\n", + finfo.size / MAX(0.001, (1.024*this_time)), + get_total_size / MAX(0.001, (1.024*get_total_time_ms)))); + } + + free(inbuf);free(outbuf); +} + +/**************************************************************************** +Append single file to tar file (or not) +***************************************************************************/ +static void do_tar(file_info *finfo) +{ + pstring rname; + + if (strequal(finfo->name,".") || strequal(finfo->name,"..")) + return; + + /* Is it on the exclude list ? */ + if (!tar_excl && clipn) { + pstring exclaim; + + strcpy(exclaim, cur_dir); + *(exclaim+strlen(exclaim)-1)='\0'; + + if (clipfind(cliplist, clipn, exclaim)) { + DEBUG(3,("Skipping directory %s\n", exclaim)); + return; + } + + strcat(exclaim, "\\"); + strcat(exclaim, finfo->name); + + if (clipfind(cliplist, clipn, exclaim)) { + DEBUG(3,("Skipping file %s\n", exclaim)); + return; + } + } + + if (finfo->mode & aDIR) + { + pstring saved_curdir; + pstring mtar_mask; + char *inbuf,*outbuf; + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + strcpy(saved_curdir,cur_dir); + + strcat(cur_dir,finfo->name); + strcat(cur_dir,"\\"); + + /* write a tar directory, don't bother with mode - just set it to + * 40755 */ + writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0"); + strcpy(mtar_mask,cur_dir); + strcat(mtar_mask,"*"); + + do_dir((char *)inbuf,(char *)outbuf,mtar_mask,attribute,do_tar,recurse); + strcpy(cur_dir,saved_curdir); + free(inbuf);free(outbuf); + } + else + { + strcpy(rname,cur_dir); + strcat(rname,finfo->name); + do_atar(rname,finfo->name,finfo); + } +} + +/**************************************************************************** +Convert from UNIX to DOS file names +***************************************************************************/ +static void unfixtarname(char *tptr, char *fp, int l) +{ + /* remove '.' from start of file name, convert from unix /'s to + * dos \'s in path. Kill any absolute path names. + */ + + if (*fp == '.') fp++; + if (*fp == '\\' || *fp == '/') fp++; + +#ifdef KANJI + while (l > 0) { + if (is_shift_jis (*fp)) { + *tptr++ = *fp++; + *tptr++ = *fp++; + l -= 2; + } else if (is_kana (*fp)) { + *tptr++ = *fp++; + l--; + } else if (*fp == '/') { + *tptr++ = '\\'; + fp++; + l--; + } else { + *tptr++ = *fp++; + l--; + } + } +#else + while (l--) { *tptr=(*fp == '/') ? '\\' : *fp; tptr++; fp++; } +#endif +} + +static void do_tarput() +{ + file_info finfo; + int nread=0, bufread; + char *inbuf,*outbuf; + int fsize=0; + int fnum; + struct timeval tp_start; + BOOL tskip=False; /* We'll take each file as it comes */ + + GetTimeOfDay(&tp_start); + + inbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + outbuf = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + + if (!inbuf || !outbuf) + { + DEBUG(0,("out of memory\n")); + return; + } + + /* + * Must read in tbufsiz dollops + */ + + /* These should be the only reads in clitar.c */ + while ((bufread=read(tarhandle, tarbuf, tbufsiz))>0) { + char *bufferp, *endofbuffer; + int chunk; + + /* Code to handle a short read. + * We always need a TBLOCK full of stuff + */ + if (bufread % TBLOCK) { + int lchunk=TBLOCK-(bufread % TBLOCK); + int lread; + + /* It's a shorty - a short read that is */ + DEBUG(3, ("Short read, read %d so far (need %d)\n", bufread, lchunk)); + + while ((lread=read(tarhandle, tarbuf+bufread, lchunk))>0) { + bufread+=lread; + if (!(lchunk-=lread)) break; + } + + /* If we've reached EOF then that must be a short file */ + if (lread<=0) break; + } + + bufferp=tarbuf; + endofbuffer=tarbuf+bufread; + + if (tskip) { + if (fsize<bufread) { + tskip=False; + bufferp+=fsize; + fsize=0; + } else { + if (fsize==bufread) tskip=False; + fsize-=bufread; + continue; + } + } + + do { + if (!fsize) + { + switch (readtarheader((union hblock *) bufferp, &finfo, cur_dir)) + { + case -2: /* something dodgy but not fatal about this */ + DEBUG(0, ("skipping %s...\n", finfo.name)); + bufferp+=TBLOCK; /* header - like a link */ + continue; + case -1: + DEBUG(0, ("abandoning restore\n")); + free(inbuf); free(outbuf); + return; + case 0: /* chksum is zero - we assume that one all zero + *header block will do for eof */ + DEBUG(0, + ("total of %d tar files restored to share\n", ntarf)); + free(inbuf); free(outbuf); + return; + default: + break; + } + + tskip=clipn + && (clipfind(cliplist, clipn, finfo.name) ^ tar_excl); + if (tskip) { + bufferp+=TBLOCK; + if (finfo.mode & aDIR) + continue; + else if ((fsize=finfo.size) % TBLOCK) { + fsize+=TBLOCK-(fsize%TBLOCK); + } + if (fsize<endofbuffer-bufferp) { + bufferp+=fsize; + fsize=0; + continue; + } else { + fsize-=endofbuffer-bufferp; + break; + } + } + + if (finfo.mode & aDIR) + { + if (!smbchkpath(finfo.name, inbuf, outbuf) + && !smbmkdir(finfo.name, inbuf, outbuf)) + { + DEBUG(0, ("abandoning restore\n")); + free(inbuf); free(outbuf); + return; + } + else + { + bufferp+=TBLOCK; + continue; + } + } + + fsize=finfo.size; + + if (ensurepath(finfo.name, inbuf, outbuf) + && !smbcreat(finfo, &fnum, inbuf, outbuf)) + { + DEBUG(0, ("abandoning restore\n")); + free(inbuf);free(outbuf); + return; + } + + DEBUG(0,("restore tar file %s of size %d bytes\n", + finfo.name,finfo.size)); + + nread=0; + if ((bufferp+=TBLOCK) >= endofbuffer) break; + } /* if (!fsize) */ + + /* write out the file in chunk sized chunks - don't + * go past end of buffer though */ + chunk=(fsize-nread < endofbuffer - bufferp) + ? fsize - nread : endofbuffer - bufferp; + + while (chunk > 0) { + int minichunk=MIN(chunk, max_xmit-200); + + if (!smbwrite(fnum, /* file descriptor */ + minichunk, /* n */ + nread, /* offset low */ + 0, /* offset high - not implemented */ + fsize-nread, /* left - only hint to server */ + bufferp, + inbuf, + outbuf)) + { + DEBUG(0, ("Error writing remote file\n")); + free(inbuf); free(outbuf); + return; + } + DEBUG(5, ("chunk writing fname=%s fnum=%d nread=%d minichunk=%d chunk=%d size=%d\n", finfo.name, fnum, nread, minichunk, chunk, fsize)); + + bufferp+=minichunk; nread+=minichunk; + chunk-=minichunk; + } + + if (nread>=fsize) + { + if (!smbshut(finfo, fnum, inbuf, outbuf)) + { + DEBUG(0, ("Error closing remote file\n")); + free(inbuf);free(outbuf); + return; + } + if (fsize % TBLOCK) bufferp+=TBLOCK - (fsize % TBLOCK); + DEBUG(5, ("bufferp is now %d (psn=%d)\n", + (long) bufferp, (long)(bufferp - tarbuf))); + ntarf++; + fsize=0; + } + } while (bufferp < endofbuffer); + } + + DEBUG(0, ("premature eof on tar file ?\n")); + DEBUG(0,("total of %d tar files restored to share\n", ntarf)); + + free(inbuf); free(outbuf); +} + +/* + * samba interactive commands + */ + +/**************************************************************************** +Blocksize command +***************************************************************************/ +void cmd_block(void) +{ + fstring buf; + int block; + + if (!next_token(NULL,buf,NULL)) + { + DEBUG(0, ("blocksize <n>\n")); + return; + } + + block=atoi(buf); + if (block < 0 || block > 65535) + { + DEBUG(0, ("blocksize out of range")); + return; + } + + blocksize=block; + DEBUG(2,("blocksize is now %d\n", blocksize)); +} + +/**************************************************************************** +command to set incremental / reset mode +***************************************************************************/ +void cmd_tarmode(void) +{ + fstring buf; + + while (next_token(NULL,buf,NULL)) { + if (strequal(buf, "full")) + tar_inc=False; + else if (strequal(buf, "inc")) + tar_inc=True; + else if (strequal(buf, "reset")) + tar_reset=True; + else if (strequal(buf, "noreset")) + tar_reset=False; + else DEBUG(0, ("tarmode: unrecognised option %s\n", buf)); + } + + DEBUG(0, ("tarmode is now %s, %s\n", + tar_inc ? "incremental" : "full", + tar_reset ? "reset" : "noreset")); +} + +/**************************************************************************** +Feeble attrib command +***************************************************************************/ +void cmd_setmode(void) +{ + char *q; + fstring buf; + pstring fname; + int attra[2]; + int direct=1; + + attra[0] = attra[1] = 0; + + if (!next_token(NULL,buf,NULL)) + { + DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n")); + return; + } + + strcpy(fname, cur_dir); + strcat(fname, buf); + + while (next_token(NULL,buf,NULL)) { + q=buf; + + while(*q) + switch (*q++) { + case '+': direct=1; + break; + case '-': direct=0; + break; + case 'r': attra[direct]|=aRONLY; + break; + case 'h': attra[direct]|=aHIDDEN; + break; + case 's': attra[direct]|=aSYSTEM; + break; + case 'a': attra[direct]|=aARCH; + break; + default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n")); + return; + } + } + + if (attra[ATTRSET]==0 && attra[ATTRRESET]==0) + { + DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n")); + return; + } + +DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET])); + (void) do_setrattr(fname, attra[ATTRSET], ATTRSET); + (void) do_setrattr(fname, attra[ATTRRESET], ATTRRESET); +} + +/**************************************************************************** +Principal command for creating / extracting +***************************************************************************/ +void cmd_tar(char *inbuf, char *outbuf) +{ + fstring buf; + char **argl; + int argcl; + + if (!next_token(NULL,buf,NULL)) + { + DEBUG(0,("tar <c|x>[IXbga] <filename>\n")); + return; + } + + argl=toktocliplist(&argcl, NULL); + if (!tar_parseargs(argcl, argl, buf, 0)) + return; + + process_tar(inbuf, outbuf); + + free(argl); +} + +/**************************************************************************** +Command line (option) version +***************************************************************************/ +int process_tar(char *inbuf, char *outbuf) +{ + initarbuf(); + switch(tar_type) { + case 'x': + do_tarput(); + free(tarbuf); + close(tarhandle); + break; + case 'r': + case 'c': + if (clipn && tar_excl) { + int i; + pstring tarmac; + + for (i=0; i<clipn; i++) { + DEBUG(0,("arg %d = %s\n", i, cliplist[i])); + + if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') { + *(cliplist[i]+strlen(cliplist[i])-1)='\0'; + } + + if (strrchr(cliplist[i], '\\')) { + pstring saved_dir; + + strcpy(saved_dir, cur_dir); + + if (*cliplist[i]=='\\') { + strcpy(tarmac, cliplist[i]); + } else { + strcpy(tarmac, cur_dir); + strcat(tarmac, cliplist[i]); + } + strcpy(cur_dir, tarmac); + *(strrchr(cur_dir, '\\')+1)='\0'; + + do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse); + strcpy(cur_dir,saved_dir); + } else { + strcpy(tarmac, cur_dir); + strcat(tarmac, cliplist[i]); + do_dir((char *)inbuf,(char *)outbuf,tarmac,attribute,do_tar,recurse); + } + } + } else { + pstring mask; + strcpy(mask,cur_dir); + strcat(mask,"\\*"); + do_dir((char *)inbuf,(char *)outbuf,mask,attribute,do_tar,recurse); + } + + if (ntarf) dotareof(tarhandle); + close(tarhandle); + free(tarbuf); + + DEBUG(0, ("tar: dumped %d tar files\n", ntarf)); + break; + } + + return(0); +} + +/**************************************************************************** +Find a token (filename) in a clip list +***************************************************************************/ +int clipfind(char **aret, int ret, char *tok) +{ + if (aret==NULL) return 0; + + /* ignore leading slashes or dots in token */ + while(strchr("/\\.", *tok)) tok++; + + while(ret--) { + char *pkey=*aret++; + + /* ignore leading slashes or dots in list */ + while(strchr("/\\.", *pkey)) pkey++; + + if (!strslashcmp(pkey, tok)) return 1; + } + + return 0; +} + +/**************************************************************************** +Parse tar arguments. Sets tar_type, tar_excl, etc. +***************************************************************************/ +int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind) +{ + char tar_clipfl='\0'; + + /* Reset back to defaults - could be from interactive version + * reset mode and archive mode left as they are though + */ + tar_type='\0'; + tar_excl=True; + + while (*Optarg) + switch(*Optarg++) { + case 'c': + tar_type='c'; + break; + case 'x': + if (tar_type=='c') { + printf("Tar must be followed by only one of c or x.\n"); + return 0; + } + tar_type='x'; + break; + case 'b': + if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) { + DEBUG(0,("Option b must be followed by valid blocksize\n")); + return 0; + } else { + Optind++; + } + break; + case 'g': + tar_inc=True; + break; + case 'N': + if (Optind>=argc) { + DEBUG(0,("Option N must be followed by valid file name\n")); + return 0; + } else { + struct stat stbuf; + extern time_t newer_than; + + if (sys_stat(argv[Optind], &stbuf) == 0) { + newer_than = stbuf.st_mtime; + DEBUG(1,("Getting files newer than %s", + asctime(LocalTime(&newer_than,GMT_TO_LOCAL)))); + Optind++; + } else { + DEBUG(0,("Error setting newer-than time\n")); + return 0; + } + } + break; + case 'a': + tar_reset=True; + break; + case 'I': + if (tar_clipfl) { + DEBUG(0,("Only one of I,X must be specified\n")); + return 0; + } + tar_clipfl='I'; + break; + case 'X': + if (tar_clipfl) { + DEBUG(0,("Only one of I,X must be specified\n")); + return 0; + } + tar_clipfl='X'; + break; + default: + DEBUG(0,("Unknown tar option\n")); + return 0; + } + + if (!tar_type) { + printf("Option T must be followed by one of c or x.\n"); + return 0; + } + + if (Optind>=argc || !strcmp(argv[Optind], "-")) { + /* Sets tar handle to either 0 or 1, as appropriate */ + tarhandle=(tar_type=='c'); + } else { + tar_excl=tar_clipfl!='X'; + + if (Optind+1<argc) { + cliplist=argv+Optind+1; + clipn=argc-Optind-1; + } + + if ((tar_type=='x' && (tarhandle = open(argv[Optind], O_RDONLY)) == -1) + || (tar_type=='c' && (tarhandle=creat(argv[Optind], 0644)) < 0)) + { + DEBUG(0,("Error opening local file %s\n",argv[Optind])); + return(0); + } + } + + return 1; +} diff --git a/source3/include/byteorder.h b/source3/include/byteorder.h new file mode 100644 index 0000000000..899cd6c499 --- /dev/null +++ b/source3/include/byteorder.h @@ -0,0 +1,80 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB Byte handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +/* + This file implements macros for machine independent short and + int manipulation +*/ + +#undef CAREFUL_ALIGNMENT + +/* we know that the 386 can handle misalignment and has the "right" + byteorder */ +#ifdef __i386__ +#define CAREFUL_ALIGNMENT 0 +#endif + +#ifndef CAREFUL_ALIGNMENT +#define CAREFUL_ALIGNMENT 1 +#endif + +#define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) +#define PVAL(buf,pos) ((unsigned)CVAL(buf,pos)) +#define SCVAL(buf,pos,val) (CVAL(buf,pos) = (val)) + + +#if CAREFUL_ALIGNMENT +#define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) +#define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16) +#define SSVALX(buf,pos,val) (CVAL(buf,pos)=(val)&0xFF,CVAL(buf,pos+1)=(val)>>8) +#define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16)) +#define SVALS(buf,pos) ((int16)SVAL(buf,pos)) +#define IVALS(buf,pos) ((int32)IVAL(buf,pos)) +#define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16)(val))) +#define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32)(val))) +#define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16)(val))) +#define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32)(val))) +#else +/* this handles things for architectures like the 386 that can handle + alignment errors */ +/* + WARNING: This section is dependent on the length of int16 and int32 + being correct +*/ +#define SVAL(buf,pos) (*(uint16 *)((char *)(buf) + (pos))) +#define IVAL(buf,pos) (*(uint32 *)((char *)(buf) + (pos))) +#define SVALS(buf,pos) (*(int16 *)((char *)(buf) + (pos))) +#define IVALS(buf,pos) (*(int32 *)((char *)(buf) + (pos))) +#define SSVAL(buf,pos,val) SVAL(buf,pos)=((uint16)(val)) +#define SIVAL(buf,pos,val) IVAL(buf,pos)=((uint32)(val)) +#define SSVALS(buf,pos,val) SVALS(buf,pos)=((int16)(val)) +#define SIVALS(buf,pos,val) IVALS(buf,pos)=((int32)(val)) +#endif + + +/* now the reverse routines - these are used in nmb packets (mostly) */ +#define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +#define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16))) + +#define RSVAL(buf,pos) SREV(SVAL(buf,pos)) +#define RIVAL(buf,pos) IREV(IVAL(buf,pos)) +#define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val)) +#define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val)) diff --git a/source3/include/charset.h b/source3/include/charset.h new file mode 100644 index 0000000000..7091732223 --- /dev/null +++ b/source3/include/charset.h @@ -0,0 +1,61 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Character set handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#ifndef CHARSET_C + +extern char *dos_char_map; +extern char *upper_char_map; +extern char *lower_char_map; +extern void add_char_string(char *s); +extern void charset_initialise(void); + +#ifdef toupper +#undef toupper +#endif + +#ifdef tolower +#undef tolower +#endif + +#ifdef isupper +#undef isupper +#endif + +#ifdef islower +#undef islower +#endif + +#ifdef isdoschar +#undef isdoschar +#endif + +#ifdef isspace +#undef isspace +#endif + +#define toupper(c) upper_char_map[(char)(c)] +#define tolower(c) lower_char_map[(char)(c)] +#define isupper(c) (((char)(c)) != tolower(c)) +#define islower(c) (((char)(c)) != toupper(c)) +#define isdoschar(c) (dos_char_map[(char)(c)] != 0) +#define isspace(c) ((c)==' ' || (c) == '\t') +#endif + diff --git a/source3/include/clitar.h b/source3/include/clitar.h new file mode 100644 index 0000000000..2305fceeec --- /dev/null +++ b/source3/include/clitar.h @@ -0,0 +1,17 @@ + +#define TBLOCK 512 +#define NAMSIZ 100 +union hblock { + char dummy[TBLOCK]; + struct header { + char name[NAMSIZ]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char linkflag; + char linkname[NAMSIZ]; + } dbuf; +}; diff --git a/source3/include/includes.h b/source3/include/includes.h new file mode 100644 index 0000000000..cc2bbbfad7 --- /dev/null +++ b/source3/include/includes.h @@ -0,0 +1,1154 @@ +#ifndef _INCLUDES_H +#define _INCLUDES_H +/* + Unix SMB/Netbios implementation. + Version 1.9. + Machine customisation and include handling + Copyright (C) Andrew Tridgell 1994-1995 + + 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. +*/ +/* + This file does all the #includes's. This makes it easier to + port to a new unix. Hopefully a port will only have to edit the Makefile + and add a section for the new unix below. +*/ + + +/* the first OS dependent section is to setup what includes will be used. + the main OS dependent section comes later on +*/ + +#ifdef ALTOS +#define NO_UTIMEH +#endif + +#ifdef MIPS +#define POSIX_H +#define NO_UTIMEH +#endif + +#ifdef sun386 +#define NO_UTIMEH +#endif + +#ifdef NEXT2 +#define NO_UTIMEH +#endif + +#ifdef NEXT3_0 +#define NO_UTIMEH +#define NO_UNISTDH +#endif + +#ifdef APOLLO +#define NO_UTIMEH +#define NO_SYSMOUNTH +#define NO_UNISTDH +#endif + +#ifdef AIX +#define NO_SYSMOUNTH +#endif + +#ifdef M88K_R3 +#define SVR3H +#define NO_RESOURCEH +#endif + +#ifdef DNIX +#define NO_SYSMOUNTH +#define NO_NETIFH +#define NO_RESOURCEH +#define PRIME_NMBD 0 +#define NO_SETGROUPS +#endif + + +#ifdef ISC +#define SYSSTREAMH +#define NO_RESOURCEH +#endif + +#ifdef QNX +#define NO_RESOURCEH +#define NO_SYSMOUNTH +#define USE_MMAP 1 +#ifdef __386__ + #define __i386__ +#endif +#endif + +#ifdef NEWS42 +#define NO_UTIMEH +#define NO_STRFTIME +#define NO_UTIMBUF +#define REPLACE_MKTIME +#define NO_TM_NAME +#endif + +#ifdef OS2 +#define NO_SYSMOUNTH +#define NO_NETIFH +#endif + +#ifdef LYNX +#define NO_SYSMOUNTH +#endif + + +#if (defined(SHADOW_PWD)||defined(OSF1_ENH_SEC)||defined(SecureWare)||defined(PWDAUTH)) +#define PASSWORD_LENGTH 16 +#endif + +/* here is the general includes section - with some ifdefs generated + by the previous section +*/ +#include "local.h" +#include <stdio.h> +#ifdef POSIX_STDLIBH +#include <posix/stdlib.h> +#else +#include <stdlib.h> +#endif +#include <ctype.h> +#include <time.h> +#ifndef NO_UTIMEH +#include <utime.h> +#endif +#include <sys/types.h> + +#ifdef SVR3H +#include <sys/statfs.h> +#include <sys/stream.h> +#include <netinet/types.h> +#include <netinet/ether.h> +#include <netinet/ip_if.h> +#endif + +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <stddef.h> +#ifdef POSIX_H +#include <posix/utime.h> +#include <bsd/sys/time.h> +#include <bsd/netinet/in.h> +#else +#include <sys/time.h> +#include <netinet/in.h> +#endif +#include <netdb.h> +#include <signal.h> +#include <errno.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <grp.h> +#ifndef NO_RESOURCEH +#include <sys/resource.h> +#endif +#ifndef NO_SYSMOUNTH +#include <sys/mount.h> +#endif +#include <pwd.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#ifndef NO_UNISTDH +#include <unistd.h> +#endif +#include <sys/wait.h> +#ifdef SYSSTREAMH +#include <sys/stream.h> +#endif +#ifndef NO_NETIFH +#ifdef POSIX_H +#include <bsd/net/if.h> +#else +#include <net/if.h> +#endif +#endif + +#if USE_MMAP +#include <sys/mman.h> +#endif + +#if defined(GETPWANAM) +#include <sys/types.h> +#include <sys/label.h> +#include <sys/audit.h> +#include <pwdadj.h> +#endif + +#if defined(SHADOW_PWD) && !defined(NETBSD) && !defined(CONVEX) +#include <shadow.h> +#endif + +/* this might be different on different systems */ +#ifdef QUOTAS +#ifdef LINUX +#ifdef __KERNEL__ +#undef __KERNEL__ +#include <sys/quota.h> +#define __KERNEL__ +#else +#include <sys/quota.h> +#endif +#include <mntent.h> +#else +#include <sys/quota.h> +#ifndef CRAY +#include <devnm.h> +#else +#include <mntent.h> +#endif +#endif +#endif + +#ifdef SYSLOG +#include <syslog.h> +#endif + + + +/*************************************************************************** +Here come some platform specific sections +***************************************************************************/ + + +#ifdef LINUX +#include <arpa/inet.h> +#include <dirent.h> +#include <string.h> +#include <sys/vfs.h> +#include <netinet/in.h> +#ifndef NO_ASMSIGNALH +#include <asm/signal.h> +#endif +#define SIGNAL_CAST (__sighandler_t) +#define USE_GETCWD +#define USE_SETSID +#define HAVE_BZERO +#define HAVE_MEMMOVE +#ifdef SHADOW_PWD +#ifndef crypt +#define crypt pw_encrypt +#endif +#endif +#endif + +#ifdef SUNOS4 +#define SIGNAL_CAST (void (*)(int)) +#include <netinet/tcp.h> +#include <dirent.h> +#include <sys/acct.h> +#include <sys/vfs.h> +#include <string.h> +#include <errno.h> +#include <sys/wait.h> +#include <signal.h> +/* #include <termios.h> */ +#ifdef sun386 +#define NO_STRFTIME +#define NO_UTIMBUF +#define mktime timelocal +typedef unsigned short mode_t; +#else +#include <utime.h> +#define NO_STRERROR +#endif +#define REPLACE_GETPASS +#define BSD_TERMIO +#endif + + +#ifdef SUNOS5 +#include <fcntl.h> +#include <dirent.h> +#include <sys/acct.h> +#include <sys/statfs.h> +#include <sys/statvfs.h> +#include <sys/filio.h> +#include <sys/sockio.h> +#include <netinet/in_systm.h> +#include <netinet/tcp.h> +#include <netinet/ip.h> +#include <string.h> +#include <arpa/inet.h> +#include <rpcsvc/ypclnt.h> +#include <crypt.h> +#include <termios.h> +extern int gettimeofday (struct timeval *, void *); +extern int gethostname (char *name, int namelen); +extern int innetgr (const char *, const char *, const char *, const char *); +#define USE_SETVBUF +#define SIGNAL_CAST (void (*)(int)) +#ifndef SYSV +#define SYSV +#endif +#define USE_WAITPID +#define REPLACE_STRLEN +#define USE_STATVFS +#define USE_GETCWD +#define USE_SETSID +#define REPLACE_GETPASS +#endif + + +#ifdef ULTRIX +#include <strings.h> +#include <nfs/nfs_clnt.h> +#include <nfs/vfs.h> +#include <netinet/tcp.h> +#ifdef ULTRIX_AUTH +#include <auth.h> +#endif +char *getwd(char *); +#define NOSTRDUP +#ifdef __STDC__ +#define SIGNAL_CAST (void(*)(int)) +#endif +#define USE_DIRECT +#endif + +#ifdef SGI +#include <netinet/tcp.h> +#include <sys/statfs.h> +#include <string.h> +#include <signal.h> +#ifndef SYSV +#define SYSV +#endif +#define SIGNAL_CAST (void (*)()) +#define STATFS4 +#define USE_WAITPID +#define USE_DIRECT +#endif + +#ifdef SGI5 +#include <netinet/tcp.h> +#include <sys/statvfs.h> +#include <string.h> +#include <signal.h> +#include <dirent.h> +#define USE_WAITPID +#define NETGROUP +#ifndef SYSV +#define SYSV +#endif +#define SIGNAL_CAST (void (*)()) +#define USE_STATVFS +#define USE_WAITPID +#endif + + +#ifdef MIPS +#include <bsd/net/soioctl.h> +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/statfs.h> +#include <sys/wait.h> +#include <sys/termio.h> +#define SIGNAL_CAST (void (*)()) +typedef int mode_t; +extern struct group *getgrnam(); +extern struct passwd *getpwnam(); +#define STATFS4 +#define NO_STRERROR +#define REPLACE_STRSTR +#endif /* MIPS */ + + + +#ifdef DGUX +#include <string.h> +#include <dirent.h> +#include <sys/statfs.h> +#include <sys/statvfs.h> +#include <fcntl.h> +#include <termios.h> +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)(int)) +#define STATFS4 +#define USE_GETCWD +#endif + + +#ifdef SVR4 +#include <string.h> +#include <sys/dir.h> +#include <dirent.h> +#include <sys/statfs.h> +#include <sys/statvfs.h> +#include <sys/vfs.h> +#include <sys/filio.h> +#include <fcntl.h> +#include <sys/sockio.h> +#include <netinet/tcp.h> +#include <stropts.h> +#include <termios.h> +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)(int)) +#define USE_STATVFS +#define USE_GETCWD +#define USE_SETSID +#endif + + +#ifdef OSF1 +#include <termios.h> +#include <strings.h> +#include <dirent.h> +char *getwd(char *); +char *mktemp(char *); /* No standard include */ +#include <netinet/in.h> +#include <arpa/inet.h> /* both for inet_ntoa */ +#define SIGNAL_CAST ( void (*) (int) ) +#define STATFS3 +#define USE_F_FSIZE +#include <netinet/tcp.h> +#ifdef OSF1_ENH_SEC +#include <pwd.h> +#include <sys/types.h> +#include <sys/security.h> +#include <prot.h> +#include <unistd.h> +#define PASSWORD_LENGTH 16 +#define NEED_AUTH_PARAMETERS +#endif /* OSF1_ENH_SEC */ +#endif + + +#ifdef CLIX +#include <dirent.h> +#define SIGNAL_CAST (void (*)()) +#include <sys/fcntl.h> +#include <sys/statfs.h> +#include <string.h> +#define NO_EID +#define USE_WAITPID +#define STATFS4 +#define NO_FSYNC +#define USE_GETCWD +#define USE_SETSID +#define REPLACE_GETPASS +#define NO_GETRLIMIT +#endif /* CLIX */ + + + +#ifdef BSDI +#include <string.h> +#include <netinet/tcp.h> +#define SIGNAL_CAST (void (*)()) +#define USE_DIRECT +#endif + + +#ifdef NETBSD +#include <strings.h> +#include <netinet/tcp.h> +/* you may not need this */ +#define NO_GETSPNAM +#define SIGNAL_CAST (void (*)()) +#define USE_DIRECT +#define REPLACE_INNETGR +#endif + + + +#ifdef FreeBSD +#include <strings.h> +#include <netinet/tcp.h> +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#define SIGNAL_CAST (void (*)()) +#define USE_DIRECT +#define REPLACE_INNETGR +#endif + + + +#ifdef AIX +#include <strings.h> +#include <sys/dir.h> +#include <sys/select.h> +#include <dirent.h> +#include <sys/statfs.h> +#include <sys/vfs.h> +#include <sys/id.h> +#include <sys/priv.h> +#include <netinet/tcp.h> +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)()) +#define DEFAULT_PRINTING PRINT_AIX +#endif + + +#ifdef HPUX +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/vfs.h> +#include <sys/types.h> +#include <sys/termios.h> +#include <netinet/tcp.h> +#ifdef HPUX_10_TRUSTED +#include <hpsecurity.h> +#include <prot.h> +#define NEED_AUTH_PARAMETERS +#endif +#define SIGNAL_CAST (void (*)(__harg)) +#define SELECT_CAST (int *) +#define SYSV +#define USE_WAITPID +#define WAIT3_CAST2 (int *) +#define USE_GETCWD +#define USE_SETSID +#define USE_SETRES +#define DEFAULT_PRINTING PRINT_HPUX +#define SIGCLD_IGNORE +#endif + + +#ifdef SEQUENT +#include <signal.h> +#include <string.h> +#include <dirent.h> +#include <sys/types.h> +#include <sys/statfs.h> +#include <sys/stat.h> +#include <sys/buf.h> +#include <sys/socket.h> +#include <unistd.h> +#include <fcntl.h> +#define SIGNAL_CAST (void (*)(int)) +#define USE_WAITPID +#define USE_GETCWD +#define NO_EID +#define STATFS4 +#define USE_DIRECT +#endif + +#ifdef NEXT2 +#include <sys/types.h> +#include <strings.h> +#include <dirent.h> +#include <sys/vfs.h> +#define bzero(b,len) memset(b,0,len) +#define mode_t int +#define NO_UTIMBUF +#include <libc.h> +#define NOSTRDUP +#define USE_DIRECT +#define USE_WAITPID +#endif + + +#ifdef NEXT3_0 +#include <strings.h> +#include <sys/dir.h> +#include <sys/vfs.h> +#define bzero(b,len) memset(b,0,len) +#define NO_UTIMBUF +#include <libc.h> +#define NOSTRDUP +#define USE_DIRECT +#define mode_t int +#define GID_TYPE int +#define gid_t int +#define SIGNAL_CAST (void (*)(int)) +#define WAIT3_CAST1 (union wait *) +#define HAVE_GMTOFF +#endif + + + +#ifdef APOLLO +#include <string.h> +#include <fcntl.h> +#include <sys/statfs.h> +#define NO_UTIMBUF +#define USE_DIRECT +#define USE_GETCWD +#define SIGNAL_CAST (void (*)()) +#define HAVE_FCNTL_LOCK 0 +#define HAVE_GETTIMEOFDAY +#define STATFS4 +#endif + + + +#ifdef SCO +#include <sys/netinet/tcp.h> +#include <sys/netinet/in_systm.h> +#include <sys/netinet/ip.h> +#include <dirent.h> +#include <string.h> +#include <fcntl.h> +#include <sys/statfs.h> +#include <sys/stropts.h> +#include <limits.h> +#ifdef EVEREST +#include <unistd.h> +#endif +#ifdef NETGROUP +#include <rpcsvc/ypclnt.h> +#endif +#ifdef SecureWare +#include <sys/security.h> +#include <sys/audit.h> +#include <prot.h> +#define crypt bigcrypt +#endif +#ifndef EVEREST + #define ftruncate(f,l) syscall(0x0a28,f,l) +#endif +#define SIGNAL_CAST (void (*)(int)) +#define USE_WAITPID +#define USE_GETCWD +#define USE_SETSID +#ifdef SCO3_2_2 +#define NO_EID +#else +#ifndef EVEREST +#define USE_IFREQ +#endif +#endif +#define STATFS4 +#define NO_FSYNC +#ifndef EVEREST +#define NO_INITGROUPS +#endif +#define HAVE_PATHCONF +#define NO_GETRLIMIT +#endif + + + +/* Definitions for RiscIX */ +#ifdef RiscIX +#define SIGNAL_CAST (void (*)(int)) +#include <sys/dirent.h> +#include <sys/acct.h> +#include <sys/vfs.h> +#include <string.h> +#include <utime.h> +#include <signal.h> +#define HAVE_GETTIMEOFDAY +#define NOSTRCASECMP +#define NOSTRDUP +#endif + + + +#ifdef ISC +#include <net/errno.h> +#include <string.h> +#include <sys/dir.h> +#include <dirent.h> +#include <sys/statfs.h> +#include <fcntl.h> +#include <sys/sioctl.h> +#include <stropts.h> +#include <limits.h> +#include <netinet/tcp.h> +#define FIONREAD FIORDCHK +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)(int)) +#define USE_GETCWD +#define USE_SETSID +#define USE_IFREQ +#define NO_FTRUNCATE +#define STATFS4 +#define NO_FSYNC +#endif + + + +#ifdef AUX +#include <fstab.h> +#include <string.h> +#include <dirent.h> +#include <sys/vfs.h> +#include <fcntl.h> +#include <termios.h> +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)(int)) +char *strdup (char *); +#define USE_GETCWD +#endif + + +#ifdef M88K_R3 +#include <string.h> +#include <dirent.h> +#include <fcntl.h> +#include <termios.h> +#define STATFS4 +#define SYSV +#define USE_WAITPID +#define SIGNAL_CAST (void (*)(int)) +char *strdup (char *); +#define USE_GETCWD +#define NO_FSYNC +#define NO_EID +#endif + + +#ifdef DNIX +#include <dirent.h> +#include <string.h> +#include <fcntl.h> +#include <sys/statfs.h> +#include <sys/stropts.h> +#define NO_GET_BROADCAST +#define USE_WAITPID +#define USE_GETCWD +#define USE_SETSID +#define STATFS4 +#define NO_EID +#define PF_INET AF_INET +#define NO_STRERROR +#define ftruncate(f,l) chsize(f,l) +#endif /* DNIX */ + +#ifdef CONVEX +#define SIGNAL_CAST (void (*)(int)) +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <dirent.h> +#include <string.h> +#include <sys/vfs.h> +#include <fcntl.h> +#define DONT_REINSTALL_SIG +#define USE_SIGBLOCK +#define USE_WAITPID +#define SIGNAL_CAST (_SigFunc_Ptr_t) +#define NO_GETSPNAM +#define HAVE_MEMMOVE +extern char *mktemp(char *); +extern int fsync(int); +extern int seteuid(uid_t); +extern int setgroups(int, int *); +extern int initgroups(char *, int); +extern int statfs(char *, struct statfs *); +extern int setegid(gid_t); +extern int getopt(int, char *const *, const char *); +extern int chroot(char *); +extern int gettimeofday(struct timeval *, struct timezone *); +extern int gethostname(char *, int); +extern char *crypt(char *, char *); +extern char *getpass(char *); +#endif + + +#ifdef CRAY +#define MAXPATHLEN 1024 +#include <dirent.h> +#include <string.h> +#include <fcntl.h> +#include <sys/statfs.h> +#define SIGNAL_CAST (void (*)(int)) +#define SIGCLD_IGNORE +#define HAVE_FCNTL_LOCK 1 +#define USE_SETSID +#define STATFS4 +#endif + + +#ifdef ALTOS +#include <unistd.h> +#include <string.h> +#include <dirent.h> +#include <sys/fcntl.h> +#include <sys/statfs.h> +#define const +#define uid_t int +#define gid_t int +#define mode_t int +#define ptrdiff_t int +#define HAVE_GETGRNAM 0 +#define NO_EID +#define NO_FSYNC +#define NO_FTRUNCATE +#define NO_GETRLIMIT +#define NO_INITGROUPS +#define NO_SELECT +#define NO_SETGROUPS +#define NO_STRERROR +#define NO_STRFTIME +#define NO_TM_NAME +#define NO_UTIMEH +#define NOSTRCASECMP +#define REPLACE_MKTIME +#define REPLACE_RENAME +#define REPLACE_STRSTR +#define STATFS4 +#define USE_GETCWD +#endif + +#ifdef QNX +#define STATFS4 +#include <sys/statfs.h> +#include <sys/select.h> +#include <signal.h> +#include <sys/dir.h> +#define SIGNAL_CAST (void (*)()) +#define USE_WAITPID +#define NO_INITGROUPS +#define NO_SETGROUPS +#define HAVE_TIMEZONE +#define USE_GETCWD +#define USE_SETSID +#define HAVE_FCNTL_LOCK 1 +#define DEFAULT_PRINTING PRINT_QNX +#endif + + +#ifdef NEWS42 +#include <string.h> +#include <dirent.h> +#include <sys/vfs.h> +#include <sys/timeb.h> +typedef int mode_t; +#endif + +#ifdef OS2 +#include <dirent.h> +#include <sys/statfs.h> +#include <string.h> +#include <limits.h> +#define SIGNAL_CAST (void (*)()) +#define HAVE_FCNTL_LOCK 0 +#define USE_WAITPID +#define NO_GET_BROADCAST +#define NO_EID +#define NO_SETGROUPS +#define NO_INITGROUPS +#define NO_CRYPT +#define NO_STATFS +#define NO_CHROOT +#define NO_CHOWN +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif + + +#ifdef LYNX +#define SIGNAL_CAST (void (*)()) +#define WAIT3_CAST1 (union wait *) +#define STATFS4 +#include <fcntl.h> +#include <resource.h> +#include <stat.h> +#include <string.h> +#include <dirent.h> +#include <sys/statfs.h> +#define USE_GETCWD +#define USE_GETSID +#endif + + +/******************************************************************* +end of the platform specific sections +********************************************************************/ + +#ifdef SecureWare +#define NEED_AUTH_PARAMETERS +#endif + +#ifdef REPLACE_GETPASS +extern char *getsmbpass(char *); +#define getpass(s) getsmbpass(s) +#endif + +#ifdef REPLACE_INNETGR +#define innetgr(group,host,user,dom) InNetGr(group,host,user,dom) +#endif + +#ifndef FD_SETSIZE +#define FD_SETSIZE 255 +#endif + +#ifndef MAXINT +#define MAXINT ((((unsigned)1)<<(sizeof(int)*8-1))-1) +#endif + +#ifndef __STDC__ +#define const +#endif + +/* Now for some other grungy stuff */ +#ifdef NO_GETSPNAM +struct spwd { /* fake shadow password structure */ + char *sp_pwdp; +}; +#endif + +#ifndef HAVE_BZERO +#ifndef bzero +#define bzero(p,s) memset(p,0,s) +#endif +#endif + +#ifndef HAVE_MEMMOVE +#ifndef memmove +#define memmove(d,s,n) MemMove(d,s,n) +#endif +#endif + +#ifdef USE_DIRECT +#include <sys/dir.h> +#endif + +/* some unixes have ENOTTY instead of TIOCNOTTY */ +#ifndef TIOCNOTTY +#ifdef ENOTTY +#define TIOCNOTTY ENOTTY +#endif +#endif + +#ifndef SIGHUP +#define SIGHUP 1 +#endif + +/* if undefined then use bsd or sysv printing */ +#ifndef DEFAULT_PRINTING +#ifdef SYSV +#define DEFAULT_PRINTING PRINT_SYSV +#else +#define DEFAULT_PRINTING PRINT_BSD +#endif +#endif + + +#ifdef AFS_AUTH +#include <afs/stds.h> +#include <afs/kautils.h> +#endif + +#ifdef DFS_AUTH +#include <dce/dce_error.h> +#include <dce/sec_login.h> +#endif + +#ifdef NO_UTIMBUF +struct utimbuf { + time_t actime; + time_t modtime; +}; +#endif + +#ifdef NO_STRERROR +#ifndef strerror +extern char *sys_errlist[]; +#define strerror(i) sys_errlist[i] +#endif +#endif + +#ifndef perror +#define perror(m) printf("%s: %s\n",m,strerror(errno)) +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 255 +#endif + +#include "version.h" +#include "smb.h" +#include "byteorder.h" +#ifdef SMB_PASSWD +#include "smbpass.h" +#endif + +#include "kanji.h" +#include "charset.h" + +#ifndef S_IFREG +#define S_IFREG 0100000 +#endif + +#ifndef S_ISREG +#define S_ISREG(x) ((S_IFREG & x)!=0) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(x) ((S_IFDIR & x)!=0) +#endif + +#ifdef UFC_CRYPT +#define crypt ufc_crypt +#endif + +#ifdef REPLACE_STRLEN +#define strlen(s) Strlen(s) +#endif + +#ifdef REPLACE_STRSTR +#define strstr(s,p) Strstr(s,p) +#endif + +#ifdef REPLACE_MKTIME +#define mktime(t) Mktime(t) +#endif + +#ifndef NGROUPS_MAX +#define NGROUPS_MAX 128 +#endif + +#ifndef EDQUOT +#define EDQUOT ENOSPC +#endif + +#ifndef HAVE_GETGRNAM +#define HAVE_GETGRNAM 1 +#endif + +#ifndef SOL_TCP +#define SOL_TCP 6 +#endif + +/* default to using ftruncate workaround as this is safer than assuming +it works and getting lots of bug reports */ +#ifndef FTRUNCATE_CAN_EXTEND +#define FTRUNCATE_CAN_EXTEND 0 +#endif + +/* maybe this unix doesn't separate RD and WR locks? */ +#ifndef F_RDLCK +#define F_RDLCK F_WRLCK +#endif + +#ifndef ENOTSOCK +#define ENOTSOCK EINVAL +#endif + +#ifndef SIGCLD +#define SIGCLD SIGCHLD +#endif + +#ifndef HAVE_FCNTL_LOCK +#define HAVE_FCNTL_LOCK 1 +#endif + +#ifndef WAIT3_CAST2 +#define WAIT3_CAST2 (struct rusage *) +#endif + +#ifndef WAIT3_CAST1 +#define WAIT3_CAST1 (int *) +#endif + +#ifndef QSORT_CAST +#define QSORT_CAST (int (*)()) +#endif + +/* this is a rough check to see if this machine has a lstat() call. + it is not guaranteed to work */ +#if !(defined(S_ISLNK) || defined(S_IFLNK)) +#define lstat stat +#endif + +/* Not all systems declare ERRNO in errno.h... and some systems #define it! */ +#ifndef errno +extern int errno; +#endif + + +#ifdef NO_EID +#define geteuid() getuid() +#define getegid() getgid() +#define seteuid(x) setuid(x) +#define setegid(x) setgid(x) +#endif + + +#if (HAVE_FCNTL_LOCK == 0) +/* since there is no locking available, system includes */ +/* for DomainOS 10.4 do not contain any of the following */ +/* #define's. So, to satisfy the compiler, add these */ +/* #define's, although they arn't really necessary. */ +#define F_GETLK 0 +#define F_SETLK 0 +#define F_WRLCK 0 +#define F_UNLCK 0 +#endif /* HAVE_FCNTL_LOCK == 0 */ + +#ifdef NOSTRCASECMP +#define strcasecmp(s1,s2) StrCaseCmp(s1,s2) +#define strncasecmp(s1,s2,n) StrnCaseCmp(s1,s2,n) +#endif + +#ifndef strcpy +#define strcpy(dest,src) StrCpy(dest,src) +#endif + + +/* possibly wrap the malloc calls */ +#if WRAP_MALLOC + +/* undo the old malloc def if necessary */ +#ifdef malloc +#define xx_old_malloc malloc +#undef malloc +#endif + +#define malloc(size) malloc_wrapped(size,__FILE__,__LINE__) + +/* undo the old realloc def if necessary */ +#ifdef realloc +#define xx_old_realloc realloc +#undef realloc +#endif + +#define realloc(ptr,size) realloc_wrapped(ptr,size,__FILE__,__LINE__) + +/* undo the old free def if necessary */ +#ifdef free +#define xx_old_free free +#undef free +#endif + +#define free(ptr) free_wrapped(ptr,__FILE__,__LINE__) + +/* and the malloc prototypes */ +void *malloc_wrapped(int,char *,int); +void *realloc_wrapped(void *,int,char *,int); +void free_wrapped(void *,char *,int); + +#endif + + +#if WRAP_MEMCPY +/* undo the old memcpy def if necessary */ +#ifdef memcpy +#define xx_old_memcpy memcpy +#undef memcpy +#endif + +#define memcpy(d,s,l) memcpy_wrapped(d,s,l,__FILE__,__LINE__) +void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line); +#endif + +#endif diff --git a/source3/include/kanji.h b/source3/include/kanji.h new file mode 100644 index 0000000000..4f18305c63 --- /dev/null +++ b/source3/include/kanji.h @@ -0,0 +1,130 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Kanji Extensions + Copyright (C) Andrew Tridgell 1992-1994 + + 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. + + Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5 + and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11 + and add all jis codes sequence at 1995.8.16 + Notes: Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp> +*/ +#ifndef _KANJI_H_ +#define _KANJI_H_ + +#ifdef KANJI + +/* FOR SHIFT JIS CODE */ +#define is_shift_jis(c) \ + ((0x81 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0x9f) \ + || (0xe0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xef)) +#define is_shift_jis2(c) \ + (0x40 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xfc \ + && ((unsigned char) (c)) != 0x7f) +#define is_kana(c) ((0xa0 <= ((unsigned char) (c)) && ((unsigned char) (c)) <= 0xdf)) + +#ifdef _KANJI_C_ +/* FOR EUC CODE */ +#define euc_kana (0x8e) +#define is_euc_kana(c) (((unsigned char) (c)) == euc_kana) +#define is_euc(c) (0xa0 < ((unsigned char) (c)) && ((unsigned char) (c)) < 0xff) + +/* FOR JIS CODE */ +/* default jis third shift code, use for output */ +#ifndef JIS_KSO +#define JIS_KSO 'B' +#endif +#ifndef JIS_KSI +#define JIS_KSI 'J' +#endif +/* in: \E$B or \E$@ */ +/* out: \E(J or \E(B or \E(H */ +#define jis_esc (0x1b) +#define jis_so (0x0e) +#define jis_so1 ('$') +#define jis_so2 ('B') +#define jis_si (0x0f) +#define jis_si1 ('(') +#define jis_si2 ('J') +#define is_esc(c) (((unsigned char) (c)) == jis_esc) +#define is_so1(c) (((unsigned char) (c)) == jis_so1) +#define is_so2(c) (((unsigned char) (c)) == jis_so2 || ((unsigned char) (c)) == '@') +#define is_si1(c) (((unsigned char) (c)) == jis_si1) +#define is_si2(c) (((unsigned char) (c)) == jis_si2 || ((unsigned char) (c)) == 'B' \ + || ((unsigned char) (c)) == 'H') +#define is_so(c) (((unsigned char) (c)) == jis_so) +#define is_si(c) (((unsigned char) (c)) == jis_si) +#define junet_kana1 ('(') +#define junet_kana2 ('I') +#define is_juk1(c) (((unsigned char) (c)) == junet_kana1) +#define is_juk2(c) (((unsigned char) (c)) == junet_kana2) + +#define _KJ_ROMAN (0) +#define _KJ_KANJI (1) +#define _KJ_KANA (2) + +/* FOR HEX */ +#define HEXTAG ':' +#define hex2bin(x) \ + ( ((int) '0' <= ((int) (x)) && ((int) (x)) <= (int)'9')? \ + (((int) (x))-(int)'0'): \ + ((int) 'a'<= ((int) (x)) && ((int) (x))<= (int) 'f')? \ + (((int) (x)) - (int)'a'+10): \ + (((int) (x)) - (int)'A'+10) ) +#define bin2hex(x) \ + ( (((int) (x)) >= 10)? (((int) (x))-10 + (int) 'a'): (((int) (x)) + (int) '0') ) + +#else /* not _KANJI_C_ */ + +extern char* (*_dos_to_unix) (const char *str, BOOL overwrite); +extern char* (*_unix_to_dos) (const char *str, BOOL overwrite); + +#define unix_to_dos (*_unix_to_dos) +#define dos_to_unix (*_dos_to_unix) + +extern char *sj_strtok (char *s1, const char *s2); +extern char *sj_strchr (const char *s, int c); +extern char *sj_strrchr (const char *s, int c); +extern char *sj_strstr (const char *s1, const char *s2); + +#define strchr sj_strchr +#define strrchr sj_strrchr +#define strstr sj_strstr +#define strtok sj_strtok + +#endif /* _KANJI_C_ */ + +#define UNKNOWN_CODE (-1) +#define SJIS_CODE (0) +#define EUC_CODE (1) +#define JIS7_CODE (2) +#define JIS8_CODE (3) +#define JUNET_CODE (4) +#define HEX_CODE (5) +#define CAP_CODE (6) +#define DOSV_CODE SJIS_CODE + +int interpret_coding_system (char *str, int def); + +#else + +#define unix_to_dos(x,y) (x) +#define dos_to_unix(x,y) (x) + +#endif /* not KANJI */ + +#endif /* _KANJI_H_ */ diff --git a/source3/include/local.h b/source3/include/local.h new file mode 100644 index 0000000000..2775453e15 --- /dev/null +++ b/source3/include/local.h @@ -0,0 +1,167 @@ +/* local definitions for file server */ +#ifndef _LOCAL_H +#define _LOCAL_H + +/* This defines the section name in the configuration file that will contain */ +/* global parameters - that is, parameters relating to the whole server, not */ +/* just services. This name is then reserved, and may not be used as a */ +/* a service name. It will default to "global" if not defined here. */ +#define GLOBAL_NAME "global" +#define GLOBAL_NAME2 "globals" + +/* This defines the section name in the configuration file that will + refer to the special "homes" service */ +#define HOMES_NAME "homes" + +/* This defines the section name in the configuration file that will + refer to the special "printers" service */ +#define PRINTERS_NAME "printers" + +/* This defines the name of the printcap file. It is MOST UNLIKELY that + this will change BUT! Specifying a file with the format of a printcap + file but containing only a subset of the printers actually in your real + printcap file is a quick-n-dirty way to allow dynamic access to a subset + of available printers. +*/ +#define PRINTCAP_NAME "/etc/printcap" + +/* set these to define the limits of the server. NOTE These are on a + per-client basis. Thus any one machine can't connect to more than + MAX_CONNECTIONS services, but any number of machines may connect at + one time. */ +#define MAX_CONNECTIONS 127 +#define MAX_OPEN_FILES 100 + +/* the max number of connections that the smbstatus program will show */ +#define MAXSTATUS 1000 + +/* max number of directories open at once */ +/* note that with the new directory code this no longer requires a + file handle per directory, but large numbers do use more memory */ +#define MAXDIR 64 + +#define WORDMAX 0xFFFF + + +/* separators for lists */ +#define LIST_SEP " \t,;:\n\r" + +#ifndef LOCKDIR +#define LOCKDIR "/tmp/samba" +#endif + +/* this is where browse lists are kept in the lock dir */ +#define SERVER_LIST "browse.dat" + +/* the print command on the server, %s is replaced with the filename */ +/* note that the -r removes the file after printing - you'll run out */ +/* of disk pretty quickly if you don't. This command is only used as */ +/* the default - it can be overridden in the configuration file. */ +#define PRINT_COMMAND "lpr -r %s" + +/* the lpq command on the server. the printername is passed as an argument */ +#ifndef LPQ_COMMAND +#define LPQ_COMMAND "lpq -P" +#endif + +/* shall guest entries in printer queues get changed to user entries, + so they can be deleted using the windows print manager? */ +#define LPQ_GUEST_TO_USER + +/* shall filenames with illegal chars in them get mangled in long + filename listings? */ +#define MANGLE_LONG_FILENAMES + +/* define this if you want to stop spoofing with .. and soft links + NOTE: This also slows down the server considerably */ +#define REDUCE_PATHS + +/* the size of the directory cache */ +#define DIRCACHESIZE 20 + +/* what type of filesystem do we want this to show up as in a NT file + manager window? */ +#define FSTYPE_STRING "Samba" + +/* we have two time standards - local and GMT. This will try to sort them out. + */ + +#define LOCAL_TO_GMT 1 +#define GMT_TO_LOCAL (-1) + +/* do you want smbd to send a 1 byte packet to nmbd to trigger it to start + when smbd starts? */ +#ifndef PRIME_NMBD +#define PRIME_NMBD 1 +#endif + +/* do you want session setups at user level security with a invalid + password to be rejected or allowed in as guest? WinNT rejects them + but it can be a pain as it means "net view" needs to use a password + + You have 3 choices: + + GUEST_SESSSETUP = 0 means session setups with an invalid password + are rejected. + + GUEST_SESSSETUP = 1 means session setups with an invalid password + are rejected, unless the username does not exist, in which case it + is treated as a guest login + + GUEST_SESSSETUP = 2 means session setups with an invalid password + are treated as a guest login + + Note that GUEST_SESSSETUP only has an effect in user or server + level security. + */ +#ifndef GUEST_SESSSETUP +#define GUEST_SESSSETUP 0 +#endif + +/* the default pager to use for the client "more" command. Users can + override this with the PAGER environment variable */ +#ifndef PAGER +#define PAGER "more" +#endif + +/* the size of the uid cache used to reduce valid user checks */ +#define UID_CACHE_SIZE 4 + +/* the following control timings of various actions. Don't change + them unless you know what you are doing. These are all in seconds */ +#define DEFAULT_SMBD_TIMEOUT (60*60*24*7) +#define SMBD_RELOAD_CHECK (10) +#define SHARE_MODES_CHECK (10) +#define SHARE_MODES_CLEAN (300) +#define IDLE_CLOSED_TIMEOUT (60) +#define DPTR_IDLE_TIMEOUT (120) +#define SMBD_SELECT_LOOP (10) +#define NMBD_SELECT_LOOP (10) +#define BROWSE_INTERVAL (60) +#define REGISTRATION_INTERVAL (10*60) +#define NMBD_INETD_TIMEOUT (120) +#define NMBD_MAX_TTL (24*60*60) +#define LPQ_LOCK_TIMEOUT (5) + +/* the following are in milliseconds */ +#define LOCK_RETRY_TIMEOUT (100) + +/* do you want to dump core (carefully!) when an internal error is + encountered? Samba will be careful to make the core file only + accessible to root */ +#define DUMP_CORE 1 + +/* what is the longest significant password available on your system? + Knowing this speeds up password searches a lot */ +#ifndef PASSWORD_LENGTH +#define PASSWORD_LENGTH 8 +#endif + +#define SMB_ALIGNMENT 1 + + +/* shall we support browse requests via a FIFO to nmbd? */ +#define ENABLE_FIFO 1 + + +#endif diff --git a/source3/include/nameserv.h b/source3/include/nameserv.h new file mode 100644 index 0000000000..168dd4ba86 --- /dev/null +++ b/source3/include/nameserv.h @@ -0,0 +1,184 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios header - version 2 + Copyright (C) Andrew Tridgell 1994-1995 + + 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. + +*/ + +#define MAX_DGRAM_SIZE 576 +#define MIN_DGRAM_SIZE 12 + +#define NMB_PORT 137 +#define DGRAM_PORT 138 +#define SMB_PORT 139 + +enum name_source {LMHOSTS, REGISTER, SELF, DNS, DNSFAIL}; +enum node_type {B_NODE=0, P_NODE=1, M_NODE=2, NBDD_NODE=3}; +enum packet_type {NMB_PACKET, DGRAM_PACKET}; + +/* a netbios name structure */ +struct nmb_name { + char name[17]; + char scope[64]; + int name_type; +}; + +/* this is the structure used for the local netbios name list */ +struct name_record +{ + struct name_record *next; + struct name_record *prev; + struct nmb_name name; + time_t death_time; + struct in_addr ip; + BOOL unique; + enum name_source source; +}; + +/* this is used by the list of domains */ +struct domain_record +{ + struct domain_record *next; + struct domain_record *prev; + fstring name; + time_t lastannounce_time; + int announce_interval; + struct in_addr bcast_ip; +}; + +/* this is used to hold the list of servers in my domain */ +struct server_record +{ + struct server_record *next; + struct server_record *prev; + fstring name; + fstring comment; + uint32 servertype; + time_t death_time; +}; + +/* a resource record */ +struct res_rec { + struct nmb_name rr_name; + int rr_type; + int rr_class; + int ttl; + int rdlength; + char rdata[MAX_DGRAM_SIZE]; +}; + +/* define a nmb packet. */ +struct nmb_packet +{ + struct { + int name_trn_id; + int opcode; + BOOL response; + struct { + BOOL bcast; + BOOL recursion_available; + BOOL recursion_desired; + BOOL trunc; + BOOL authoritative; + } nm_flags; + int rcode; + int qdcount; + int ancount; + int nscount; + int arcount; + } header; + + struct { + struct nmb_name question_name; + int question_type; + int question_class; + } question; + + struct res_rec *answers; + struct res_rec *nsrecs; + struct res_rec *additional; +}; + + +/* a datagram - this normally contains SMB data in the data[] array */ +struct dgram_packet { + struct { + int msg_type; + struct { + enum node_type node_type; + BOOL first; + BOOL more; + } flags; + int dgm_id; + struct in_addr source_ip; + int source_port; + int dgm_length; + int packet_offset; + } header; + struct nmb_name source_name; + struct nmb_name dest_name; + int datasize; + char data[MAX_DGRAM_SIZE]; +}; + +/* define a structure used to queue packets. this will be a linked + list of nmb packets */ +struct packet_struct +{ + struct packet_struct *next; + struct packet_struct *prev; + struct in_addr ip; + int port; + int fd; + time_t timestamp; + enum packet_type packet_type; + union { + struct nmb_packet nmb; + struct dgram_packet dgram; + } packet; +}; + + +/* this defines a list of network interfaces */ +struct net_interface { + struct net_interface *next; + struct in_addr ip; + struct in_addr bcast; + struct in_addr netmask; +}; + + +/* prototypes */ +void free_nmb_packet(struct nmb_packet *nmb); +void free_packet(struct packet_struct *packet); +struct packet_struct *read_packet(int fd,enum packet_type packet_type); +BOOL send_packet(struct packet_struct *p); +struct packet_struct *receive_packet(int fd,enum packet_type type,int timeout); +void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope); +BOOL name_query(int fd,char *name,int name_type, + BOOL bcast,BOOL recurse, + struct in_addr to_ip, struct in_addr *ip,void (*fn)()); +BOOL name_status(int fd,char *name,int name_type,BOOL recurse, + struct in_addr to_ip,char *master,char *rname, + void (*fn)()); +BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len, + char *srcname,char *dstname, + int src_type,int dest_type, + struct in_addr dest_ip, + struct in_addr src_ip); +char *namestr(struct nmb_name *n); diff --git a/source3/include/smb.h b/source3/include/smb.h new file mode 100644 index 0000000000..b7faffa9e9 --- /dev/null +++ b/source3/include/smb.h @@ -0,0 +1,1006 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ +#ifndef _SMB_H +#define _SMB_H + +#ifndef MAX_CONNECTIONS +#define MAX_CONNECTIONS 127 +#endif + +#ifndef MAX_OPEN_FILES +#define MAX_OPEN_FILES 50 +#endif + +#ifndef GUEST_ACCOUNT +#define GUEST_ACCOUNT "nobody" +#endif + +#define BUFFER_SIZE (0xFFFF) +#define SAFETY_MARGIN 1024 + +#ifndef EXTERN +# define EXTERN extern +#endif + +#define False (0) +#define True (1) +#define BOOLSTR(b) ((b) ? "Yes" : "No") +#define BITSETB(ptr,bit) ((((char *)ptr)[0] & (1<<(bit)))!=0) +#define BITSETW(ptr,bit) ((SVAL(ptr,0) & (1<<(bit)))!=0) +#define PTR_DIFF(p1,p2) ((ptrdiff_t)(((char *)(p1)) - (char *)(p2))) + +typedef int BOOL; + +/* + Samba needs type definitions for int16, int32, uint16 and uint32. + + Normally these are signed and unsigned 16 and 32 bit integers, but + they actually only need to be at least 16 and 32 bits + respectively. Thus if your word size is 8 bytes just defining them + as signed and unsigned int will work. +*/ + +/* afs/stds.h defines int16 and int32 */ +#ifndef AFS_AUTH +typedef short int16; +typedef int int32; +#endif + +#ifndef uint16 +typedef unsigned short uint16; +#endif + +#ifndef uint32 +typedef unsigned int uint32; +#endif + +#define SIZEOFWORD 2 + +#ifndef DEF_CREATE_MASK +#define DEF_CREATE_MASK (0755) +#endif + +#ifndef DEFAULT_PIPE_TIMEOUT +#define DEFAULT_PIPE_TIMEOUT 10000000 /* Ten seconds */ +#endif + +/* debugging code */ +#ifndef SYSLOG +#define DEBUG(level,body) ((DEBUGLEVEL>=(level))?(Debug1 body):0) +#else +EXTERN int syslog_level; + +#define DEBUG(level,body) ((DEBUGLEVEL>=(level))? \ + (syslog_level = (level), Debug1 body):0) +#endif + +#define DIR_STRUCT_SIZE 43 + +/* these define all the command types recognised by the server - there +are lots of gaps so probably there are some rare commands that are not +implemented */ + +#define pSETDIR '\377' + +/* these define the attribute byte as seen by DOS */ +#define aRONLY (1L<<0) +#define aHIDDEN (1L<<1) +#define aSYSTEM (1L<<2) +#define aVOLID (1L<<3) +#define aDIR (1L<<4) +#define aARCH (1L<<5) + +/* deny modes */ +#define DENY_DOS 0 +#define DENY_ALL 1 +#define DENY_WRITE 2 +#define DENY_READ 3 +#define DENY_NONE 4 +#define DENY_FCB 7 + +/* share types */ +#define STYPE_DISKTREE 0 /* Disk drive */ +#define STYPE_PRINTQ 1 /* Spooler queue */ +#define STYPE_DEVICE 2 /* Serial device */ +#define STYPE_IPC 3 /* Interprocess communication (IPC) */ + +/* SMB X/Open error codes for the ERRdos error class */ +#define ERRbadfunc 1 /* Invalid function (or system call) */ +#define ERRbadfile 2 /* File not found (pathname error) */ +#define ERRbadpath 3 /* Directory not found */ +#define ERRnofids 4 /* Too many open files */ +#define ERRnoaccess 5 /* Access denied */ +#define ERRbadfid 6 /* Invalid fid */ +#define ERRnomem 8 /* Out of memory */ +#define ERRbadmem 9 /* Invalid memory block address */ +#define ERRbadenv 10 /* Invalid environment */ +#define ERRbadaccess 12 /* Invalid open mode */ +#define ERRbaddata 13 /* Invalid data (only from ioctl call) */ +#define ERRres 14 /* reserved */ +#define ERRbaddrive 15 /* Invalid drive */ +#define ERRremcd 16 /* Attempt to delete current directory */ +#define ERRdiffdevice 17 /* rename/move across different filesystems */ +#define ERRnofiles 18 /* no more files found in file search */ +#define ERRbadshare 32 /* Share mode on file conflict with open mode */ +#define ERRlock 33 /* Lock request conflicts with existing lock */ +#define ERRfilexists 80 /* File in operation already exists */ +#define ERRbadpipe 230 /* Named pipe invalid */ +#define ERRpipebusy 231 /* All instances of pipe are busy */ +#define ERRpipeclosing 232 /* named pipe close in progress */ +#define ERRnotconnected 233 /* No process on other end of named pipe */ +#define ERRmoredata 234 /* More data to be returned */ +#define ERROR_EAS_DIDNT_FIT 275 /* Extended attributes didn't fit */ +#define ERROR_EAS_NOT_SUPPORTED 282 /* Extended attributes not suppored */ +#define ERRunknownlevel 124 +#define ERRunknownipc 2142 + + +/* here's a special one from observing NT */ +#define ERRnoipc 66 /* don't support ipc */ + +/* Error codes for the ERRSRV class */ + +#define ERRerror 1 /* Non specific error code */ +#define ERRbadpw 2 /* Bad password */ +#define ERRbadtype 3 /* reserved */ +#define ERRaccess 4 /* No permissions to do the requested operation */ +#define ERRinvnid 5 /* tid invalid */ +#define ERRinvnetname 6 /* Invalid servername */ +#define ERRinvdevice 7 /* Invalid device */ +#define ERRqfull 49 /* Print queue full */ +#define ERRqtoobig 50 /* Queued item too big */ +#define ERRinvpfid 52 /* Invalid print file in smb_fid */ +#define ERRsmbcmd 64 /* Unrecognised command */ +#define ERRsrverror 65 /* smb server internal error */ +#define ERRfilespecs 67 /* fid and pathname invalid combination */ +#define ERRbadlink 68 /* reserved */ +#define ERRbadpermits 69 /* Access specified for a file is not valid */ +#define ERRbadpid 70 /* reserved */ +#define ERRsetattrmode 71 /* attribute mode invalid */ +#define ERRpaused 81 /* Message server paused */ +#define ERRmsgoff 82 /* Not receiving messages */ +#define ERRnoroom 83 /* No room for message */ +#define ERRrmuns 87 /* too many remote usernames */ +#define ERRtimeout 88 /* operation timed out */ +#define ERRnoresource 89 /* No resources currently available for request. */ +#define ERRtoomanyuids 90 /* too many userids */ +#define ERRbaduid 91 /* bad userid */ +#define ERRuseMPX 250 /* temporarily unable to use raw mode, use MPX mode */ +#define ERRuseSTD 251 /* temporarily unable to use raw mode, use standard mode */ +#define ERRcontMPX 252 /* resume MPX mode */ +#define ERRbadPW /* reserved */ +#define ERRnosupport 0xFFFF +#define ERRunknownsmb 22 /* from NT 3.5 response */ + + +/* Error codes for the ERRHRD class */ + +#define ERRnowrite 19 /* read only media */ +#define ERRbadunit 20 /* Unknown device */ +#define ERRnotready 21 /* Drive not ready */ +#define ERRbadcmd 22 /* Unknown command */ +#define ERRdata 23 /* Data (CRC) error */ +#define ERRbadreq 24 /* Bad request structure length */ +#define ERRseek 25 +#define ERRbadmedia 26 +#define ERRbadsector 27 +#define ERRnopaper 28 +#define ERRwrite 29 /* write fault */ +#define ERRread 30 /* read fault */ +#define ERRgeneral 31 /* General hardware failure */ +#define ERRwrongdisk 34 +#define ERRFCBunavail 35 +#define ERRsharebufexc 36 /* share buffer exceeded */ +#define ERRdiskfull 39 + + +typedef char pstring[1024]; +typedef char fstring[128]; +typedef fstring string; + +typedef struct +{ + int size; + int mode; + int uid; + int gid; + /* these times are normally kept in GMT */ + time_t mtime; + time_t atime; + time_t ctime; + pstring name; +} file_info; + + +/* Structure used when SMBwritebmpx is active */ +typedef struct + { + int wr_total_written; /* So we know when to discard this */ + int32 wr_timeout; + int32 wr_errclass; + int32 wr_error; /* Cached errors */ + BOOL wr_mode; /* write through mode) */ + BOOL wr_discard; /* discard all further data */ + } write_bmpx_struct; + +typedef struct +{ + int cnum; + int fd; + int pos; + int size; + int mode; + char *mmap_ptr; + int mmap_size; + write_bmpx_struct *wbmpx_ptr; + time_t open_time; + BOOL open; + BOOL can_lock; + BOOL can_read; + BOOL can_write; + BOOL share_mode; + BOOL share_pending; + BOOL print_file; + BOOL modified; + char *name; +} files_struct; + + +struct uid_cache { + int entries; + int list[UID_CACHE_SIZE]; +}; + +typedef struct +{ + int service; + BOOL force_user; + int uid; /* uid of user who *opened* this connection */ + int gid; /* gid of user who *opened* this connection */ + struct uid_cache uid_cache; + void *dirptr; + BOOL open; + BOOL printer; + BOOL ipc; + BOOL read_only; + BOOL admin_user; + char *dirpath; + char *connectpath; + char *origpath; + char *user; /* name of user who *opened* this connection */ + /* following groups stuff added by ih */ + /* This groups info is valid for the user that *opened* the connection */ + int ngroups; + gid_t *groups; + int *igroups; /* an integer version - some OSes are broken :-( */ + time_t lastused; + BOOL used; + int num_files_open; +} connection_struct; + + +typedef struct +{ + int uid; /* uid of a validated user */ + int gid; /* gid of a validated user */ + fstring name; /* name of a validated user */ + BOOL guest; + /* following groups stuff added by ih */ + /* This groups info is needed for when we become_user() for this uid */ + int user_ngroups; + gid_t *user_groups; + int *user_igroups; /* an integer version - some OSes are broken :-( */ +} user_struct; + + +enum {LPQ_QUEUED,LPQ_PAUSED,LPQ_SPOOLING,LPQ_PRINTING}; + +typedef struct +{ + int job; + int size; + int status; + int priority; + time_t time; + char user[30]; + char file[100]; +} print_queue_struct; + +enum {LPSTAT_OK, LPSTAT_STOPPED, LPSTAT_ERROR}; + +typedef struct +{ + fstring message; + int status; +} print_status_struct; + + +/* this is used for smbstatus */ +struct connect_record +{ + int magic; + int pid; + int cnum; + int uid; + int gid; + char name[24]; + char addr[24]; + char machine[128]; + time_t start; +}; + + +#define LOCKING_VERSION 2 + +/* these are useful macros for checking validity of handles */ +#define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < MAX_OPEN_FILES)) +#define OPEN_FNUM(fnum) (VALID_FNUM(fnum) && Files[fnum].open) +#define VALID_CNUM(cnum) (((cnum) >= 0) && ((cnum) < MAX_CONNECTIONS)) +#define OPEN_CNUM(cnum) (VALID_CNUM(cnum) && Connections[cnum].open) +#define IS_IPC(cnum) (VALID_CNUM(cnum) && Connections[cnum].ipc) +#define FNUM_OK(fnum,c) (OPEN_FNUM(fnum) && (c)==Files[fnum].cnum) + +#define CHECK_FNUM(fnum,c) if (!FNUM_OK(fnum,c)) \ + return(ERROR(ERRDOS,ERRbadfid)) +#define CHECK_READ(fnum) if (!Files[fnum].can_read) \ + return(ERROR(ERRDOS,ERRbadaccess)) +#define CHECK_WRITE(fnum) if (!Files[fnum].can_write) \ + return(ERROR(ERRDOS,ERRbadaccess)) +#define CHECK_ERROR(fnum) if (HAS_CACHED_ERROR(fnum)) \ + return(CACHED_ERROR(fnum)) + +/* translates a connection number into a service number */ +#define SNUM(cnum) (Connections[cnum].service) + +/* access various service details */ +#define SERVICE(snum) (lp_servicename(snum)) +#define PRINTCAP (lp_printcapname()) +#define PRINTCOMMAND(snum) (lp_printcommand(snum)) +#define PRINTERNAME(snum) (lp_printername(snum)) +#define CAN_WRITE(cnum) (OPEN_CNUM(cnum) && !Connections[cnum].read_only) +#define VALID_SNUM(snum) (lp_snum_ok(snum)) +#define GUEST_OK(snum) (VALID_SNUM(snum) && lp_guest_ok(snum)) +#define GUEST_ONLY(snum) (VALID_SNUM(snum) && lp_guest_only(snum)) +#define CAN_SETDIR(snum) (!lp_no_set_dir(snum)) +#define CAN_PRINT(cnum) (OPEN_CNUM(cnum) && lp_print_ok(SNUM(cnum))) +#define POSTSCRIPT(cnum) (OPEN_CNUM(cnum) && lp_postscript(SNUM(cnum))) +#define MAP_HIDDEN(cnum) (OPEN_CNUM(cnum) && lp_map_hidden(SNUM(cnum))) +#define MAP_SYSTEM(cnum) (OPEN_CNUM(cnum) && lp_map_system(SNUM(cnum))) +#define MAP_ARCHIVE(cnum) (OPEN_CNUM(cnum) && lp_map_archive(SNUM(cnum))) +#define CREATE_MODE(cnum) (lp_create_mode(SNUM(cnum)) | 0700) +#ifdef SMB_PASSWD +#define SMBENCRYPT() (lp_encrypted_passwords()) +#else +#define SMBENCRYPT() (False) +#endif + +/* the basic packet size, assuming no words or bytes */ +#define smb_size 39 + +/* offsets into message for common items */ +#define smb_com 8 +#define smb_rcls 9 +#define smb_reh 10 +#define smb_err 11 +#define smb_flg 13 +#define smb_flg2 14 +#define smb_reb 13 +#define smb_tid 28 +#define smb_pid 30 +#define smb_uid 32 +#define smb_mid 34 +#define smb_wct 36 +#define smb_vwv 37 +#define smb_vwv0 37 +#define smb_vwv1 39 +#define smb_vwv2 41 +#define smb_vwv3 43 +#define smb_vwv4 45 +#define smb_vwv5 47 +#define smb_vwv6 49 +#define smb_vwv7 51 +#define smb_vwv8 53 +#define smb_vwv9 55 +#define smb_vwv10 57 +#define smb_vwv11 59 +#define smb_vwv12 61 +#define smb_vwv13 63 +#define smb_vwv14 65 +#define smb_vwv15 67 +#define smb_vwv16 69 +#define smb_vwv17 71 + + +/* the complete */ +#define SMBmkdir 0x00 /* create directory */ +#define SMBrmdir 0x01 /* delete directory */ +#define SMBopen 0x02 /* open file */ +#define SMBcreate 0x03 /* create file */ +#define SMBclose 0x04 /* close file */ +#define SMBflush 0x05 /* flush file */ +#define SMBunlink 0x06 /* delete file */ +#define SMBmv 0x07 /* rename file */ +#define SMBgetatr 0x08 /* get file attributes */ +#define SMBsetatr 0x09 /* set file attributes */ +#define SMBread 0x0A /* read from file */ +#define SMBwrite 0x0B /* write to file */ +#define SMBlock 0x0C /* lock byte range */ +#define SMBunlock 0x0D /* unlock byte range */ +#define SMBctemp 0x0E /* create temporary file */ +#define SMBmknew 0x0F /* make new file */ +#define SMBchkpth 0x10 /* check directory path */ +#define SMBexit 0x11 /* process exit */ +#define SMBlseek 0x12 /* seek */ +#define SMBtcon 0x70 /* tree connect */ +#define SMBtconX 0x75 /* tree connect and X*/ +#define SMBtdis 0x71 /* tree disconnect */ +#define SMBnegprot 0x72 /* negotiate protocol */ +#define SMBdskattr 0x80 /* get disk attributes */ +#define SMBsearch 0x81 /* search directory */ +#define SMBsplopen 0xC0 /* open print spool file */ +#define SMBsplwr 0xC1 /* write to print spool file */ +#define SMBsplclose 0xC2 /* close print spool file */ +#define SMBsplretq 0xC3 /* return print queue */ +#define SMBsends 0xD0 /* send single block message */ +#define SMBsendb 0xD1 /* send broadcast message */ +#define SMBfwdname 0xD2 /* forward user name */ +#define SMBcancelf 0xD3 /* cancel forward */ +#define SMBgetmac 0xD4 /* get machine name */ +#define SMBsendstrt 0xD5 /* send start of multi-block message */ +#define SMBsendend 0xD6 /* send end of multi-block message */ +#define SMBsendtxt 0xD7 /* send text of multi-block message */ + +/* Core+ protocol */ +#define SMBlockread 0x13 /* Lock a range and read */ +#define SMBwriteunlock 0x14 /* Unlock a range then write */ +#define SMBreadbraw 0x1a /* read a block of data with no smb header */ +#define SMBwritebraw 0x1d /* write a block of data with no smb header */ +#define SMBwritec 0x20 /* secondary write request */ +#define SMBwriteclose 0x2c /* write a file then close it */ + +/* dos extended protocol */ +#define SMBreadBraw 0x1A /* read block raw */ +#define SMBreadBmpx 0x1B /* read block multiplexed */ +#define SMBreadBs 0x1C /* read block (secondary response) */ +#define SMBwriteBraw 0x1D /* write block raw */ +#define SMBwriteBmpx 0x1E /* write block multiplexed */ +#define SMBwriteBs 0x1F /* write block (secondary request) */ +#define SMBwriteC 0x20 /* write complete response */ +#define SMBsetattrE 0x22 /* set file attributes expanded */ +#define SMBgetattrE 0x23 /* get file attributes expanded */ +#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */ +#define SMBtrans 0x25 /* transaction - name, bytes in/out */ +#define SMBtranss 0x26 /* transaction (secondary request/response) */ +#define SMBioctl 0x27 /* IOCTL */ +#define SMBioctls 0x28 /* IOCTL (secondary request/response) */ +#define SMBcopy 0x29 /* copy */ +#define SMBmove 0x2A /* move */ +#define SMBecho 0x2B /* echo */ +#define SMBopenX 0x2D /* open and X */ +#define SMBreadX 0x2E /* read and X */ +#define SMBwriteX 0x2F /* write and X */ +#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */ +#define SMBffirst 0x82 /* find first */ +#define SMBfunique 0x83 /* find unique */ +#define SMBfclose 0x84 /* find close */ +#define SMBinvalid 0xFE /* invalid command */ + +/* Extended 2.0 protocol */ +#define SMBtrans2 0x32 /* TRANS2 protocol set */ +#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */ +#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */ +#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */ +#define SMBulogoffX 0x74 /* user logoff */ + + +/* these are the TRANS2 sub commands */ +#define TRANSACT2_OPEN 0 +#define TRANSACT2_FINDFIRST 1 +#define TRANSACT2_FINDNEXT 2 +#define TRANSACT2_QFSINFO 3 +#define TRANSACT2_SETFSINFO 4 +#define TRANSACT2_QPATHINFO 5 +#define TRANSACT2_SETPATHINFO 6 +#define TRANSACT2_QFILEINFO 7 +#define TRANSACT2_SETFILEINFO 8 +#define TRANSACT2_FSCTL 9 +#define TRANSACT2_IOCTL 10 +#define TRANSACT2_FINDNOTIFYFIRST 11 +#define TRANSACT2_FINDNOTIFYNEXT 12 +#define TRANSACT2_MKDIR 13 + + +/* these are the trans2 sub fields for primary requests */ +#define smb_tpscnt smb_vwv0 +#define smb_tdscnt smb_vwv1 +#define smb_mprcnt smb_vwv2 +#define smb_mdrcnt smb_vwv3 +#define smb_msrcnt smb_vwv4 +#define smb_flags smb_vwv5 +#define smb_timeout smb_vwv6 +#define smb_pscnt smb_vwv9 +#define smb_psoff smb_vwv10 +#define smb_dscnt smb_vwv11 +#define smb_dsoff smb_vwv12 +#define smb_suwcnt smb_vwv13 +#define smb_setup smb_vwv14 +#define smb_setup0 smb_setup +#define smb_setup1 (smb_setup+2) +#define smb_setup2 (smb_setup+4) + +/* these are for the secondary requests */ +#define smb_spscnt smb_vwv2 +#define smb_spsoff smb_vwv3 +#define smb_spsdisp smb_vwv4 +#define smb_sdscnt smb_vwv5 +#define smb_sdsoff smb_vwv6 +#define smb_sdsdisp smb_vwv7 +#define smb_sfid smb_vwv8 + +/* and these for responses */ +#define smb_tprcnt smb_vwv0 +#define smb_tdrcnt smb_vwv1 +#define smb_prcnt smb_vwv3 +#define smb_proff smb_vwv4 +#define smb_prdisp smb_vwv5 +#define smb_drcnt smb_vwv6 +#define smb_droff smb_vwv7 +#define smb_drdisp smb_vwv8 + +/* where to find the base of the SMB packet proper */ +#define smb_base(buf) (((char *)(buf))+4) + + +#define SUCCESS 0 /* The request was successful. */ +#define ERRDOS 0x01 /* Error is from the core DOS operating system set. */ +#define ERRSRV 0x02 /* Error is generated by the server network file manager.*/ +#define ERRHRD 0x03 /* Error is an hardware error. */ +#define ERRCMD 0xFF /* Command was not in the "SMB" format. */ + +/* structure used to hold the incoming hosts info */ +struct from_host { + char *name; /* host name */ + char *addr; /* host address */ + struct sockaddr_in *sin; /* their side of the link */ +}; + +/* and a few prototypes */ +BOOL user_ok(char *user,int snum); +int sys_rename(char *from, char *to); +int sys_select(fd_set *fds,struct timeval *tval); +int sys_unlink(char *fname); +int sys_open(char *fname,int flags,int mode); +DIR *sys_opendir(char *dname); +int sys_stat(char *fname,struct stat *sbuf); +int sys_lstat(char *fname,struct stat *sbuf); +int sys_mkdir(char *dname,int mode); +int sys_rmdir(char *dname); +int sys_chdir(char *dname); +int sys_utime(char *fname,struct utimbuf *times); +int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize); +void lpq_reset(int); +void status_printjob(int cnum,int snum,int jobid,int status); +void DirCacheAdd(char *path,char *name,char *dname,int snum); +char *DirCacheCheck(char *path,char *name,int snum); +void DirCacheFlush(int snum); +int interpret_character_set(char *str, int def); +char *dos2unix_format(char *, BOOL); +char *unix2dos_format(char *, BOOL); +BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type); +void BlockSignals(BOOL block); +void msleep(int t); +int file_lock(char *name,int timeout); +void file_unlock(int fd); +int find_service(char *service); +int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew); +int smb_offset(char *p,char *buf); +void sync_file(int fnum); +int PutUniCode(char *dst,char *src); +void map_username(char *user); +void close_low_fds(void); +void clean_share_files(void); +int write_socket(int fd,char *buf,int len); +char *readdirname(void *p); +int dos_chmod(int cnum,char *fname,int mode,struct stat *st); +int smb_numwords(char *buf); +int get_share_mode(int cnum,struct stat *sbuf,int *pid); +void del_share_mode(int fnum); +BOOL set_share_mode(int fnum,int mode); +int DSTDiff(time_t t); +void TimeInit(void); +void put_long_date(char *p,time_t t); +time_t interpret_long_date(char *p); +void dptr_idlecnum(int cnum); +void dptr_closecnum(int cnum); +void init_dptrs(void); +void fault_setup(); +void set_socket_options(int fd, char *options); +void putip(void *dest,void *src); +void standard_sub_basic(char *s); +void *OpenDir(char *name); +void CloseDir(void *p); +char *ReadDirName(void *p); +BOOL SeekDir(void *p,int pos); +int TellDir(void *p); +int write_data(int fd,char *buffer,int N); +BOOL server_cryptkey(char *buf); +BOOL server_validate(char *buf); +BOOL become_service(int cnum,BOOL do_chdir); +BOOL snum_used(int snum); +BOOL reload_services(BOOL test); +void reopen_logs(void); +int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align); +int str_checksum(char *s); +time_t file_modtime(char *fname); +BOOL do_match(char *str, char *regexp, int case_sig); +BOOL is_a_socket(int fd); +void _smb_setlen(char *buf,int len); +void valid_initialise(void); +BOOL is_8_3(char *fname); +BOOL is_mangled(char *s); +void standard_sub(int cnum,char *s); +void del_printqueue(int cnum,int snum,int jobid); +BOOL strisnormal(char *s); +BOOL check_mangled_stack(char *s); +int sys_chown(char *fname,int uid,int gid); +int sys_chroot(char *dname); +BOOL next_token(char **ptr,char *buff,char *sep); +void invalidate_uid(int uid); +char *fgets_slash(char *s,int maxlen,FILE *f); +int read_udp_socket(int fd,char *buf,int len); +void exit_server(char *reason); +BOOL process_exists(int pid); +BOOL chgpasswd(char *name,char *oldpass,char *newpass); +void array_promote(char *array,int elsize,int element); +void string_replace(char *s,char oldc,char newc); +BOOL user_in_list(char *user,char *list); +BOOL string_sub(char *s,char *pattern,char *insert); +char *StrnCpy(char *dest,const char *src,int n); +char *validated_username(int vuid); +BOOL set_user_password(char *user,char *oldpass,char *newpass); +int smb_buf_ofs(char *buf); +char *skip_string(char *buf,int n); +BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset); +int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact); +int write_file(int fnum,char *data,int n); +BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode); +int seek_file(int fnum,int pos); +BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode); +int get_printqueue(int snum,int cnum,print_queue_struct **queue,print_status_struct *status); +void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev); +int setup_groups(char *user,int uid, int gid, int *p_ngroups, + int **p_igroups, gid_t **p_groups); +int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid); +char *dptr_path(int key); +char *dptr_wcard(int key); +BOOL dptr_set_wcard(int key, char *wcard); +BOOL dptr_set_attr(int key, uint16 attr); +uint16 dptr_attr(int key); +void dptr_close(int key); +void dptr_closepath(char *path,int pid); +int dptr_create(int cnum,char *path, BOOL expect_close,int pid); +BOOL dptr_fill(char *buf,unsigned int key); +BOOL dptr_zero(char *buf); +void *dptr_fetch(char *buf,int *num); +void *dptr_fetch_lanman2(char *params,int dptr_num); +BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend); +void open_file(int fnum,int cnum,char *fname,int flags,int mode); +void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun,int mode,int *Access,int *action); +void close_file(int fnum); +int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize); +int reply_trans(char *inbuf,char *outbuf); +char *ufc_crypt(char *key,char *salt); +BOOL authorise_login(int snum,char *user,char *password, int pwlen, + BOOL *guest,BOOL *force,int vuid); +void add_session_user(char *user); +int valid_uid(int uid); +user_struct *get_valid_user_struct(int uid); +BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL nt_password); +void register_uid(int uid,int gid,char *name,BOOL guest); +BOOL fromhost(int sock,struct from_host *f); +BOOL strhasupper(char *s); +BOOL strhaslower(char *s); +int disk_free(char *path,int *bsize,int *dfree,int *dsize); +char *uidtoname(int uid); +char *gidtoname(int gid); +int get_share_mode_byname(int cnum,char *fname,int *pid); +int get_share_mode_by_fnum(int cnum,int fnum,int *pid); +BOOL check_file_sharing(int cnum,char *fname); +char *StrCpy(char *dest,char *src); +int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line); +time_t make_unix_date2(void *date_ptr); +int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line); +mode_t unix_mode(int cnum,int dosmode); +BOOL check_name(char *name,int cnum); +int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line); +int find_free_file(void ); +BOOL unix_convert(char *name,int cnum); +void unix_convert_lanman2(char *s,char *home,BOOL case_is_sig); +void print_file(int fnum); +int read_smb_length(int fd,char *inbuf,int timeout); +int read_predict(int fd,int offset,char *buf,char **ptr,int num); +void invalidate_read_prediction(int fd); +void do_read_prediction(); +BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear); +BOOL yield_connection(int cnum,char *name,int max_connections); +int count_chars(char *s,char c); +int smbrun(char *,char *); +BOOL name_map_mangle(char *OutName,BOOL need83,int snum); +struct hostent *Get_Hostbyname(char *name); +struct passwd *Get_Pwnam(char *user,BOOL allow_change); +void Abort(void); +void *Realloc(void *p,int size); +void smb_setlen(char *buf,int len); +int set_message(char *buf,int num_words,int num_bytes,BOOL zero); +BOOL check_access(int snum); +BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups); +BOOL string_set(char **dest,char *src); +BOOL string_init(char **dest,char *src); +void string_free(char **s); +char *attrib_string(int mode); +void unix_format(char *fname); +BOOL directory_exist(char *dname,struct stat *st); +time_t make_unix_date3(void *date_ptr); +void put_dos_date3(char *buf,int offset,time_t unixdate); +void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date); +BOOL in_list(char *s,char *list,BOOL case_sensitive); +void strupper(char *s); +BOOL file_exist(char *fname,struct stat *sbuf); +int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt, long time_out, BOOL exact); +void close_sockets(void ); +BOOL send_smb(int fd,char *buffer); +BOOL send_keepalive(int client); +int read_data(int fd,char *buffer,int N); +int smb_len(char *buf); +BOOL receive_smb(int fd,char *buffer,int timeout); +void show_msg(char *buf); +BOOL big_endian(void ); +BOOL become_user(int cnum, int uid); +BOOL unbecome_user(void); +void become_daemon(void); +BOOL reduce_name(char *s,char *dir,BOOL widelinks); +void strlower(char *s); +void strnorm(char *s); +char *smb_buf(char *buf); +char *smb_trans2_param(char *buf); +char *smb_trans2_data(char *buf); +BOOL strequal(char *,char *); +BOOL strnequal(char *,char *,int n); +BOOL strcsequal(char *,char *); +BOOL mask_match( char *str, char *regexp, int case_sig, BOOL trans2); +int dos_mode(int ,char *,struct stat *); +char *timestring(); +BOOL ip_equal(struct in_addr ip1,struct in_addr ip2); +BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type); +char *get_home_dir(char *); +int set_filelen(int fd, long len); +void put_dos_date(char *buf,int offset,time_t unixdate); +void put_dos_date2(char *buf,int offset,time_t unixdate); +int lp_keepalive(void); +int name_len(char *s); +void dos_clean_name(char *s); +void unix_clean_name(char *s); +time_t make_unix_date(void *date_ptr); +BOOL lanman2_match( char *str, char *regexp, int case_sig, BOOL autoext); +BOOL trim_string(char *s,char *front,char *back); +int byte_checksum(char *buf,int len); +BOOL yesno(char *p); +uint32 file_size(char *file_name); +void dos_format(char *fname); +char *GetWd(char *s); +int name_mangle(char *in,char *out,char name_type); +int name_len(char *s); +void create_mangled_stack(int size); +int name_extract(char *buf,int ofs,char *name); +void get_broadcast(struct in_addr *if_ipaddr, struct in_addr *if_bcast, struct in_addr *if_nmask); +BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client); +#ifdef __STDC__ +int Debug1(char *, ...); +#else +int Debug1(); +#endif +BOOL check_hosts_equiv(char *user); +int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize); +void close_cnum(int cnum,int uid); +char *smb_errstr(char *inbuf); +void GetTimeOfDay(struct timeval *tval); +struct tm *LocalTime(time_t *t,int); +int TimeDiff(time_t t); +BOOL set_filetime(char *fname,time_t mtime); +char *dirname_dos(char *path,char *buf); +BOOL get_myname(char *myname,struct in_addr *ip); +void expand_mask(char *Mask, BOOL); +BOOL sane_unix_date(time_t unixdate); +time_t start_of_month(void); +char *smb_fn_name(int cnum); +void get_machine_info(void); +int open_socket_in(int type, int port, int dlevel); +int open_socket_out(int type,struct in_addr *addr, int port ); +struct in_addr *interpret_addr2(char *str); +BOOL zero_ip(struct in_addr ip); +int read_max_udp(int fd,char *buffer,int bufsize,int maxtime); +int interpret_protocol(char *str,int def); +int interpret_security(char *str,int def); +int ChDir(char *path); +int smb_buflen(char *buf); +unsigned long interpret_addr(char *str); +void mangle_name_83(char *s); +BOOL lp_casesignames(void); +void setup_logging(char *pname,BOOL interactive); +#ifdef DFS_AUTH +void dfs_unlogin(void); +extern int dcelogin_atmost_once; +#endif +#if AJT +void ajt_panic(void); +#endif +#ifdef NOSTRDUP +char *strdup(char *s); +#endif +#ifdef REPLACE_STRLEN +int Strlen(char *); +#endif +#ifdef REPLACE_STRSTR +char *Strstr(char *s, char *p); +#endif + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif + +#ifndef ABS +#define ABS(a) ((a)>0?(a):(-(a))) +#endif + +#ifndef SIGNAL_CAST +#define SIGNAL_CAST +#endif + +#ifndef SELECT_CAST +#define SELECT_CAST +#endif + + +/* Some POSIX definitions for those without */ + +#ifndef S_IFDIR +#define S_IFDIR 0x4000 +#endif +#ifndef S_ISDIR +#define S_ISDIR(mode) ((mode & 0xF000) == S_IFDIR) +#endif +#ifndef S_IRWXU +#define S_IRWXU 00700 /* read, write, execute: owner */ +#endif +#ifndef S_IRUSR +#define S_IRUSR 00400 /* read permission: owner */ +#endif +#ifndef S_IWUSR +#define S_IWUSR 00200 /* write permission: owner */ +#endif +#ifndef S_IXUSR +#define S_IXUSR 00100 /* execute permission: owner */ +#endif +#ifndef S_IRWXG +#define S_IRWXG 00070 /* read, write, execute: group */ +#endif +#ifndef S_IRGRP +#define S_IRGRP 00040 /* read permission: group */ +#endif +#ifndef S_IWGRP +#define S_IWGRP 00020 /* write permission: group */ +#endif +#ifndef S_IXGRP +#define S_IXGRP 00010 /* execute permission: group */ +#endif +#ifndef S_IRWXO +#define S_IRWXO 00007 /* read, write, execute: other */ +#endif +#ifndef S_IROTH +#define S_IROTH 00004 /* read permission: other */ +#endif +#ifndef S_IWOTH +#define S_IWOTH 00002 /* write permission: other */ +#endif +#ifndef S_IXOTH +#define S_IXOTH 00001 /* execute permission: other */ +#endif + + +/* these are used in NetServerEnum to choose what to receive */ +#define SV_TYPE_WORKSTATION 0x00000001 +#define SV_TYPE_SERVER 0x00000002 +#define SV_TYPE_SQLSERVER 0x00000004 +#define SV_TYPE_DOMAIN_CTRL 0x00000008 +#define SV_TYPE_DOMAIN_BAKCTRL 0x00000010 +#define SV_TYPE_TIME_SOURCE 0x00000020 +#define SV_TYPE_AFP 0x00000040 +#define SV_TYPE_NOVELL 0x00000080 +#define SV_TYPE_DOMAIN_MEMBER 0x00000100 +#define SV_TYPE_PRINTQ_SERVER 0x00000200 +#define SV_TYPE_DIALIN_SERVER 0x00000400 +#define SV_TYPE_SERVER_UNIX 0x00000800 +#define SV_TYPE_NT 0x00001000 +#define SV_TYPE_WFW 0x00002000 +#define SV_TYPE_SERVER_MFPN 0x00004000 +#define SV_TYPE_SERVER_NT 0x00008000 +#define SV_TYPE_POTENTIAL_BROWSER 0x00010000 +#define SV_TYPE_BACKUP_BROWSER 0x00020000 +#define SV_TYPE_MASTER_BROWSER 0x00040000 +#define SV_TYPE_DOMAIN_MASTER 0x00080000 +#define SV_TYPE_SERVER_OSF 0x00100000 +#define SV_TYPE_SERVER_VMS 0x00200000 +#define SV_TYPE_ALTERNATE_XPORT 0x20000000 +#define SV_TYPE_LOCAL_LIST_ONLY 0x40000000 +#define SV_TYPE_DOMAIN_ENUM 0x80000000 +#define SV_TYPE_ALL 0xFFFFFFFF + + + +/* protocol types. It assumes that higher protocols include lower protocols + as subsets */ +enum protocol_types {PROTOCOL_NONE,PROTOCOL_CORE,PROTOCOL_COREPLUS,PROTOCOL_LANMAN1,PROTOCOL_LANMAN2,PROTOCOL_NT1}; + +/* security levels */ +enum security_types {SEC_SHARE,SEC_USER,SEC_SERVER}; + +/* printing types */ +enum printing_types {PRINT_BSD,PRINT_SYSV,PRINT_AIX,PRINT_HPUX,PRINT_QNX}; + + +/* case handling */ +enum case_handling {CASE_LOWER,CASE_UPPER}; + + +/* Macros to get at offsets within smb_lkrng and smb_unlkrng + structures. We cannot define these as actual structures + due to possible differences in structure packing + on different machines/compilers. */ + +#define SMB_LPID_OFFSET(indx) (10 * (indx)) +#define SMB_LKOFF_OFFSET(indx) ( 2 + (10 * (indx))) +#define SMB_LKLEN_OFFSET(indx) ( 6 + (10 * (indx))) + +/* Macro to cache an error in a write_bmpx_struct */ +#define CACHE_ERROR(w,c,e) ((w)->wr_errclass = (c), (w)->wr_error = (e), \ + w->wr_discard = True, -1) +/* Macro to test if an error has been cached for this fnum */ +#define HAS_CACHED_ERROR(fnum) (Files[(fnum)].open && \ + Files[(fnum)].wbmpx_ptr && \ + Files[(fnum)].wbmpx_ptr->wr_discard) +/* Macro to turn the cached error into an error packet */ +#define CACHED_ERROR(fnum) cached_error_packet(inbuf,outbuf,fnum,__LINE__) + +/* these are the datagram types */ +#define DGRAM_DIRECT_UNIQUE 0x10 + +#define ERROR(class,x) error_packet(inbuf,outbuf,class,x,__LINE__) + +/* this is how errors are generated */ +#define UNIXERROR(defclass,deferror) unix_error_packet(inbuf,outbuf,defclass,deferror,__LINE__) + +#define ROUNDUP(x,g) (((x)+((g)-1))&~((g)-1)) + +#endif +/* _SMB_H */ diff --git a/source3/include/trans2.h b/source3/include/trans2.h new file mode 100644 index 0000000000..cc366ccaea --- /dev/null +++ b/source3/include/trans2.h @@ -0,0 +1,241 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB transaction2 handling + Copyright (C) Jeremy Allison 1994 + + Extensively modified by Andrew Tridgell, 1995 + + 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. +*/ + +#ifndef _TRANS2_H_ +#define _TRANS2_H_ + +/* Define the structures needed for the trans2 calls. */ + +/******************************************************* + For DosFindFirst/DosFindNext - level 1 + +MAXFILENAMELEN = 255; +FDATE == uint16 +FTIME == uint16 +ULONG == uint32 +USHORT == uint16 + +typedef struct _FILEFINDBUF { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 FDATE fdateCreation; +2 FTIME ftimeCreation; +4 FDATE fdateLastAccess; +6 FTIME ftimeLastAccess; +8 FDATE fdateLastWrite; +10 FTIME ftimeLastWrite; +12 ULONG cbFile file length in bytes +16 ULONG cbFileAlloc size of file allocation unit +20 USHORT attrFile +22 UCHAR cchName length of name to follow (not including zero) +23 UCHAR achName[MAXFILENAMELEN]; Null terminated name +} FILEFINDBUF; +*********************************************************/ + +#define l1_fdateCreation 0 +#define l1_fdateLastAccess 4 +#define l1_fdateLastWrite 8 +#define l1_cbFile 12 +#define l1_cbFileAlloc 16 +#define l1_attrFile 20 +#define l1_cchName 22 +#define l1_achName 23 + +/********************************************************** +For DosFindFirst/DosFindNext - level 2 + +typedef struct _FILEFINDBUF2 { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 FDATE fdateCreation; +2 FTIME ftimeCreation; +4 FDATE fdateLastAccess; +6 FTIME ftimeLastAccess; +8 FDATE fdateLastWrite; +10 FTIME ftimeLastWrite; +12 ULONG cbFile file length in bytes +16 ULONG cbFileAlloc size of file allocation unit +20 USHORT attrFile +22 ULONG cbList Extended attribute list (always 0) +26 UCHAR cchName length of name to follow (not including zero) +27 UCHAR achName[MAXFILENAMELEN]; Null terminated name +} FILEFINDBUF2; +*************************************************************/ + +#define l2_fdateCreation 0 +#define l2_fdateLastAccess 4 +#define l2_fdateLastWrite 8 +#define l2_cbFile 12 +#define l2_cbFileAlloc 16 +#define l2_attrFile 20 +#define l2_cbList 22 +#define l2_cchName 26 +#define l2_achName 27 + + +/********************************************************** +For DosFindFirst/DosFindNext - level 260 + +typedef struct _FILEFINDBUF260 { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 ULONG NextEntryOffset; +4 ULONG FileIndex; +8 LARGE_INTEGER CreationTime; +16 LARGE_INTEGER LastAccessTime; +24 LARGE_INTEGER LastWriteTime; +32 LARGE_INTEGER ChangeTime; +40 LARGE_INTEGER EndOfFile; +48 LARGE_INTEGER AllocationSize; +56 ULONG FileAttributes; +60 ULONG FileNameLength; +64 ULONG EaSize; +68 CHAR ShortNameLength; +70 UNICODE ShortName[12]; +94 UNICODE FileName[]; +*************************************************************/ + +#define l260_achName 94 + + +/********************************************************** +For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/ +DosSetFileInfo - level 1 + +typedef struct _FILESTATUS { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 FDATE fdateCreation; +2 FTIME ftimeCreation; +4 FDATE fdateLastAccess; +6 FTIME ftimeLastAccess; +8 FDATE fdateLastWrite; +10 FTIME ftimeLastWrite; +12 ULONG cbFile file length in bytes +16 ULONG cbFileAlloc size of file allocation unit +20 USHORT attrFile +} FILESTATUS; +*************************************************************/ + +/* Use the l1_ defines from DosFindFirst */ + +/********************************************************** +For DosQueryPathInfo/DosQueryFileInfo/DosSetPathInfo/ +DosSetFileInfo - level 2 + +typedef struct _FILESTATUS2 { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 FDATE fdateCreation; +2 FTIME ftimeCreation; +4 FDATE fdateLastAccess; +6 FTIME ftimeLastAccess; +8 FDATE fdateLastWrite; +10 FTIME ftimeLastWrite; +12 ULONG cbFile file length in bytes +16 ULONG cbFileAlloc size of file allocation unit +20 USHORT attrFile +22 ULONG cbList Length of EA's (0) +} FILESTATUS2; +*************************************************************/ + +/* Use the l2_ #defines from DosFindFirst */ + +/********************************************************** +For DosQFSInfo/DosSetFSInfo - level 1 + +typedef struct _FSALLOCATE { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 ULONG idFileSystem id of file system +4 ULONG cSectorUnit number of sectors per allocation unit +8 ULONG cUnit number of allocation units +12 ULONG cUnitAvail Available allocation units +16 USHORT cbSector bytes per sector +} FSALLOCATE; +*************************************************************/ + +#define l1_idFileSystem 0 +#define l1_cSectorUnit 4 +#define l1_cUnit 8 +#define l1_cUnitAvail 12 +#define l1_cbSector 16 + +/********************************************************** +For DosQFSInfo/DosSetFSInfo - level 2 + +typedef struct _FSINFO { +Byte offset Type name description +-------------+-------+-------------------+-------------- +0 FDATE vol_fdateCreation +2 FTIME vol_ftimeCreation +4 UCHAR vol_cch length of volume name (excluding NULL) +5 UCHAR vol_szVolLabel[12] volume name +} FSINFO; +*************************************************************/ + +#define SMB_QUERY_FS_LABEL_INFO 0x101 +#define SMB_QUERY_FS_VOLUME_INFO 0x102 +#define SMB_QUERY_FS_SIZE_INFO 0x103 +#define SMB_QUERY_FS_DEVICE_INFO 0x104 +#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105 + + +#define l2_vol_fdateCreation 0 +#define l2_vol_cch 4 +#define l2_vol_szVolLabel 5 + + +#define SMB_QUERY_FILE_BASIC_INFO 0x101 +#define SMB_QUERY_FILE_STANDARD_INFO 0x102 +#define SMB_QUERY_FILE_EA_INFO 0x103 +#define SMB_QUERY_FILE_NAME_INFO 0x104 +#define SMB_QUERY_FILE_ALLOCATION_INFO 0x105 +#define SMB_QUERY_FILE_END_OF_FILEINFO 0x106 +#define SMB_QUERY_FILE_ALL_INFO 0x107 +#define SMB_QUERY_FILE_ALT_NAME_INFO 0x108 +#define SMB_QUERY_FILE_STREAM_INFO 0x109 + +#define SMB_FIND_FILE_DIRECTORY_INFO 0x101 +#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102 +#define SMB_FIND_FILE_NAMES_INFO 0x103 +#define SMB_FIND_FILE_BOTH_DIRECTORY_INFO 0x104 + +#define SMB_SET_FILE_BASIC_INFO 0x101 +#define SMB_SET_FILE_DISPOSITION_INFO 0x102 +#define SMB_SET_FILE_ALLOCATION_INFO 0x103 +#define SMB_SET_FILE_END_OF_FILE_INFO 0x104 + +#define DIRLEN_GUESS (45+MAX(l1_achName,l2_achName)) + +/* Function prototypes */ + + +int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize); + +int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize); + +#endif + + + diff --git a/source3/include/version.h b/source3/include/version.h new file mode 100644 index 0000000000..9ad8b7d44b --- /dev/null +++ b/source3/include/version.h @@ -0,0 +1 @@ +#define VERSION "1.9.16alpha1" diff --git a/source3/include/vt_mode.h b/source3/include/vt_mode.h new file mode 100644 index 0000000000..85b481122e --- /dev/null +++ b/source3/include/vt_mode.h @@ -0,0 +1,48 @@ +/* vt_mode.h */ +/* +support vtp-sessions + +written by Christian A. Lademann <cal@zls.com> +*/ + +/* +02.05.95:cal:ported to samba-1.9.13 +*/ + +#ifndef __vt_mode_h__ +# define __vt_mode_h__ + +# define VT_CLOSED 0 +# define VT_OPEN 1 + +# define MS_NONE 0 +# define MS_PTY 1 +# define MS_STREAM 2 +# define MS_VTY 3 + +# define VT_MAXREAD 32 + + +# undef EXTERN + +# ifndef __vt_mode_c__ +# define EXTERN extern +# define DEFAULT(v) +# else +# define EXTERN +# define DEFAULT(v) =(v) +# endif + + EXTERN int VT_Status DEFAULT(VT_CLOSED), + VT_Fd DEFAULT(-1), + VT_ChildPID DEFAULT(-1); + + EXTERN BOOL VT_Mode DEFAULT(False), + VT_ChildDied DEFAULT(False); + + EXTERN char *VT_Line DEFAULT(NULL); + +# undef EXTERN + + +#endif /* __vt_mode_h__ */ diff --git a/source3/lib/access.c b/source3/lib/access.c new file mode 100644 index 0000000000..14a84b2fb4 --- /dev/null +++ b/source3/lib/access.c @@ -0,0 +1,389 @@ +/* +This module is an adaption of code from the tcpd-1.4 package written +by Wietse Venema, Eindhoven University of Technology, The Netherlands. + +The code is used here with permission. + +The code has been considerably changed from the original. Bug reports +should be sent to Andrew.Tridgell@anu.edu.au +*/ + +#include "includes.h" +#include "loadparm.h" + +#define ALLOW_PURE_ADDRESSES + +extern int DEBUGLEVEL; + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long)~0) +#endif + + +#define FROM_ADDRLEN (4*3+3+1) +#define Good True +#define Bad False + +#define CLIENT_MATCH client_match + +/* Delimiters for lists of daemons or clients. */ + +static char sep[] = ", \t"; + +/* Constants to be used in assignments only, not in comparisons... */ + +#define YES 1 +#define NO 0 +#define FAIL (-1) + +/* Forward declarations. */ +BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client); +static int list_match(char *list,char *item, int (*match_fn)()); +static int client_match(char *tok,char *item); +static int string_match(char *tok,char *s); +static int masked_match(char *tok, char *slash, char *s); +static int matchname(char *remotehost,struct in_addr addr); +BOOL fromhost(int sock,struct from_host *f); + + +/* Size of logical line buffer. */ +#define BUFLEN 2048 + + +/* return true if access should be allowed to a service*/ +BOOL check_access(int snum) +{ + extern int Client; + extern struct from_host Client_info; + char *denyl,*allowl; + BOOL ret = False; + + denyl = lp_hostsdeny(snum); + if (denyl) denyl = strdup(denyl); + + allowl = lp_hostsallow(snum); + if (allowl) allowl = strdup(allowl); + + + fromhost(Client,&Client_info); + + if ((!denyl || *denyl==0) && (!allowl || *allowl==0)) + ret = True; + + if (!ret) + { + if (!fromhost(Client,&Client_info)) + DEBUG(0,("ERROR: Can't get from_host info\n")); + else + { + if (allow_access(denyl,allowl,&Client_info)) + { + if (snum >= 0) + DEBUG(2,("Allowed connection from %s (%s) to %s\n", + Client_info.name,Client_info.addr, + lp_servicename(snum))); + ret = True; + } + else + if (snum >= 0) + DEBUG(0,("Denied connection from %s (%s) to %s\n", + Client_info.name,Client_info.addr, + lp_servicename(snum))); + } + } + + if (denyl) free(denyl); + if (allowl) free(allowl); + return(ret); +} + + +/* return true if access should be allowed */ +BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client) +{ + /* if theres no deny list and no allow list then allow access */ + if ((!deny_list || *deny_list == 0) && (!allow_list || *allow_list == 0)) + return(True); + + /* if there is an allow list but no deny list then allow only hosts + on the allow list */ + if (!deny_list || *deny_list == 0) + return(list_match(allow_list,(char *)client,CLIENT_MATCH)); + + /* if theres a deny list but no allow list then allow + all hosts not on the deny list */ + if (!allow_list || *allow_list == 0) + return(!list_match(deny_list,(char *)client,CLIENT_MATCH)); + + /* if there are both type of list then allow all hosts on the allow list */ + if (list_match(allow_list,(char *)client,CLIENT_MATCH)) + return (True); + + /* if there are both type of list and it's not on the allow then + allow it if its not on the deny */ + if (list_match(deny_list,(char *)client,CLIENT_MATCH)) + return (False); + + return (True); +} + +/* list_match - match an item against a list of tokens with exceptions */ +/* (All modifications are marked with the initials "jkf") */ +static int list_match(char *list,char *item, int (*match_fn)()) +{ + char *tok; + char *listcopy; /* jkf */ + int match = NO; + + /* + * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match() + * overwriting the list given as its first parameter. + */ + + /* jkf -- can get called recursively with NULL list */ + listcopy = (list == 0) ? (char *)0 : strdup(list); + + /* + * Process tokens one at a time. We have exhausted all possible matches + * when we reach an "EXCEPT" token or the end of the list. If we do find + * a match, look for an "EXCEPT" list and recurse to determine whether + * the match is affected by any exceptions. + */ + + for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) { + if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */ + break; + if ((match = (*match_fn) (tok, item))) /* YES or FAIL */ + break; + } + /* Process exceptions to YES or FAIL matches. */ + + if (match != NO) { + while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT")) + /* VOID */ ; + if (tok == 0 || list_match((char *) 0, item, match_fn) == NO) { + if (listcopy != 0) free(listcopy); /* jkf */ + return (match); + } + } + + if (listcopy != 0) free(listcopy); /* jkf */ + return (NO); +} + + +/* client_match - match host name and address against token */ +static int client_match(char *tok,char *item) +{ + struct from_host *client = (struct from_host *) item; + int match; + + /* + * Try to match the address first. If that fails, try to match the host + * name if available. + */ + + if ((match = string_match(tok, client->addr)) == 0) + if (client->name[0] != 0) + match = string_match(tok, client->name); + return (match); +} + +/* string_match - match string against token */ +static int string_match(char *tok,char *s) +{ + int tok_len; + int str_len; + char *cut; + + /* + * Return YES if a token has the magic value "ALL". Return FAIL if the + * token is "FAIL". If the token starts with a "." (domain name), return + * YES if it matches the last fields of the string. If the token has the + * magic value "LOCAL", return YES if the string does not contain a "." + * character. If the token ends on a "." (network number), return YES if + * it matches the first fields of the string. If the token begins with a + * "@" (netgroup name), return YES if the string is a (host) member of + * the netgroup. Return YES if the token fully matches the string. If the + * token is a netnumber/netmask pair, return YES if the address is a + * member of the specified subnet. + */ + + if (tok[0] == '.') { /* domain: match last fields */ + if ((str_len = strlen(s)) > (tok_len = strlen(tok)) + && strcasecmp(tok, s + str_len - tok_len) == 0) + return (YES); + } else if (tok[0] == '@') { /* netgroup: look it up */ +#ifdef NETGROUP + static char *mydomain = NULL; + char *hostname = NULL; + BOOL netgroup_ok = False; + + if (!mydomain) yp_get_default_domain(&mydomain); + + if (!(hostname = strdup(s))) { + DEBUG(1,("out of memory for strdup!\n")); + return NO; + } + + netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); + + DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", + hostname, + mydomain, + tok+1, + BOOLSTR(netgroup_ok))); + +#ifdef NETGROUP_INSECURE + /* if you really want netgroups that match non qualified names + then define NETGROUP_INSECURE. It can, however, be a big + security hole */ + { + char *clnt_domain; + if (!netgroup_ok && (clnt_domain=strchr(hostname,'.'))) { + *clnt_domain++ = '\0'; + netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); + } + } +#endif + + free(hostname); + + if (netgroup_ok) return(YES); +#else + DEBUG(0,("access: netgroup support is not configured")); + return (NO); +#endif + } else if (strcasecmp(tok, "ALL") == 0) { /* all: match any */ + return (YES); + } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */ + return (FAIL); + } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ + if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) + return (YES); + } else if (!strcasecmp(tok, s)) { /* match host name or address */ + return (YES); + } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */ + if (strncmp(tok, s, tok_len) == 0) + return (YES); + } else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */ + if (isdigit(s[0]) && masked_match(tok, cut, s)) + return (YES); + } + return (NO); +} + +/* masked_match - match address against netnumber/netmask */ +static int masked_match(char *tok, char *slash, char *s) +{ + unsigned long net; + unsigned long mask; + unsigned long addr; + + if ((addr = interpret_addr(s)) == INADDR_NONE) + return (NO); + *slash = 0; + net = interpret_addr(tok); + *slash = '/'; + if (net == INADDR_NONE || (mask = interpret_addr(slash + 1)) == INADDR_NONE) { + DEBUG(0,("access: bad net/mask access control: %s", tok)); + return (NO); + } + return ((addr & mask) == net); +} + + +/* fromhost - find out what is at the other end of a socket */ +BOOL fromhost(int sock,struct from_host *f) +{ + static struct sockaddr sa; + struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); + struct hostent *hp; + int length = sizeof(sa); + static char addr_buf[FROM_ADDRLEN]; + static char name_buf[MAXHOSTNAMELEN]; + BOOL takeAddressAsHostname = False; + + if (getpeername(sock, &sa, &length) < 0) + { + DEBUG(0,("getpeername failed\n")); + return(False); + } + + f->sin = sockin; + f->addr = strcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr)); + + /* Look up the remote host name. */ + if ((hp = gethostbyaddr((char *) &sockin->sin_addr, + sizeof(sockin->sin_addr), + AF_INET)) == 0) { + DEBUG(1,("Gethostbyaddr failed for %s\n",addr_buf)); +#ifdef ALLOW_PURE_ADDRESSES + takeAddressAsHostname = True; +#else + return(False); +#endif + } + + /* Save the host name. A later gethostbyxxx() call may clobber it. */ + f->name = StrnCpy(name_buf, + takeAddressAsHostname? f->addr : hp->h_name, + sizeof(name_buf) - 1); + + /* + * Verify that the host name does not belong to someone else. If host + * name verification fails, pretend that the host name lookup failed. + */ + if (!takeAddressAsHostname && !matchname(f->name, sockin->sin_addr)) + { + DEBUG(0,("Matchname failed\n")); + return(False); + } + + return(True); +} + +/* matchname - determine if host name matches IP address */ +static int matchname(char *remotehost,struct in_addr addr) +{ + struct hostent *hp; + int i; + + if ((hp = Get_Hostbyname(remotehost)) == 0) { + DEBUG(0,("Get_Hostbyname(%s): lookup failure", remotehost)); + return (Bad); + } + + /* + * Make sure that gethostbyname() returns the "correct" host name. + * Unfortunately, gethostbyname("localhost") sometimes yields + * "localhost.domain". Since the latter host name comes from the + * local DNS, we just have to trust it (all bets are off if the local + * DNS is perverted). We always check the address list, though. + */ + + if (strcasecmp(remotehost, hp->h_name) + && strcasecmp(remotehost, "localhost")) { + DEBUG(0,("host name/name mismatch: %s != %s", + remotehost, hp->h_name)); + return (Bad); + } + + /* Look up the host address in the address list we just got. */ + for (i = 0; hp->h_addr_list[i]; i++) { + if (memcmp(hp->h_addr_list[i], (caddr_t) & addr, sizeof(addr)) == 0) + return (Good); + } + + /* + * The host name does not map to the original host address. Perhaps + * someone has compromised a name server. More likely someone botched + * it, but that could be dangerous, too. + */ + + DEBUG(0,("host name/address mismatch: %s != %s", + inet_ntoa(addr), hp->h_name)); + return (Bad); +} + + diff --git a/source3/lib/charcnv.c b/source3/lib/charcnv.c new file mode 100644 index 0000000000..049390f2a4 --- /dev/null +++ b/source3/lib/charcnv.c @@ -0,0 +1,126 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Character set conversion Extensions + Copyright (C) Andrew Tridgell 1992-1994 + + 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. + +*/ +#include "includes.h" +extern int DEBUGLEVEL; + +static char cvtbuf[1024]; + +static mapsinited = 0; + +static char unix2dos[256]; +static char dos2unix[256]; + +static void initmaps() { + int k; + + for (k = 0; k < 256; k++) unix2dos[k] = k; + for (k = 0; k < 256; k++) dos2unix[k] = k; + + mapsinited = 1; +} + +static void update_map(char * str) { + char *p; + + for (p = str; *p; p++) { + if (p[1]) { + unix2dos[(unsigned char)*p] = p[1]; + dos2unix[(unsigned char)p[1]] = *p; + p++; + } + } +} + +static void initiso() { + + if (!mapsinited) initmaps(); + + update_map("\241\255\242\233\243\234\244\236\245\235\246\272\247\025\250\251"); + update_map("\251\273\252\246\253\256\254\252\255\274\256\310\257\257\260\370"); + update_map("\261\361\262\375\263\264\264\265\265\266\266\024\267\371\270\267"); + update_map("\271\270\272\247\273\275\274\254\275\253\276\276\277\250\200\277"); + update_map("\301\300\302\301\303\302\304\216\305\217\306\222\307\200\310\303"); + update_map("\311\220\312\305\313\306\314\307\315\315\316\317\317\320\320\311"); + update_map("\321\245\322\321\323\322\324\323\325\324\326\231\327\312\330\325"); + update_map("\331\326\332\327\333\330\334\232\335\313\336\314\337\341\340\205"); + update_map("\341\240\342\203\343\331\344\204\345\206\346\221\347\207\350\212"); + update_map("\351\202\352\210\353\211\354\215\355\241\356\214\357\213\360\316"); + update_map("\361\244\362\225\363\242\364\223\365\332\366\224\367\366\370\362"); + update_map("\371\227\372\243\373\226\374\201\375\304\376\263\377\230"); +} + +/* + * Convert unix to dos + */ +char * +unix2dos_format(char *str,BOOL overwrite) +{ + char *p; + char *dp; + + if (!mapsinited) initmaps(); + if (overwrite) { + for (p = str; *p; p++) *p = unix2dos[(unsigned char)*p]; + return str; + } else { + for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = unix2dos[(unsigned char)*p]; + *dp = 0; + return cvtbuf; + } +} + +/* + * Convert dos to unix + */ +char * +dos2unix_format (char *str, BOOL overwrite) +{ + char *p; + char *dp; + + if (!mapsinited) initmaps(); + if (overwrite) { + for (p = str; *p; p++) *p = dos2unix[(unsigned char)*p]; + return str; + } else { + for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = dos2unix[(unsigned char)*p]; + *dp = 0; + return cvtbuf; + } +} + + +/* + * Interpret character set. + */ +int +interpret_character_set (char *str, int def) +{ + + if (strequal (str, "iso8859-1")) { + initiso(); + return def; + } else { + DEBUG(0,("unrecognized character set\n")); + } + return def; +} diff --git a/source3/lib/charset.c b/source3/lib/charset.c new file mode 100644 index 0000000000..ada3ef790a --- /dev/null +++ b/source3/lib/charset.c @@ -0,0 +1,111 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Character set handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#define CHARSET_C +#include "includes.h" + +extern int DEBUGLEVEL; + +char xx_dos_char_map[256]; +char xx_upper_char_map[256]; +char xx_lower_char_map[256]; + +char *dos_char_map = NULL; +char *upper_char_map = NULL; +char *lower_char_map = NULL; + +static void add_dos_char(int lower, int upper) +{ + DEBUG(6,("Adding chars 0%o 0%o\n",lower,upper)); + if (lower) dos_char_map[(char)lower] = 1; + if (upper) dos_char_map[(char)upper] = 1; + if (lower && upper) { + lower_char_map[(char)upper] = (char)lower; + upper_char_map[(char)lower] = (char)upper; + } +} + +/**************************************************************************** +initialise the charset arrays +****************************************************************************/ +void charset_initialise(void) +{ + int i; + + dos_char_map = &xx_dos_char_map[128]; + upper_char_map = &xx_upper_char_map[128]; + lower_char_map = &xx_lower_char_map[128]; + + for (i= -128;i<=127;i++) { + dos_char_map[(char)i] = 0; + } + + for (i=0;i<=127;i++) { + if (isalnum((char)i) || strchr("._^$~!#%&-{}()@'`",(char)i)) + add_dos_char(i,0); + } + + for (i= -128;i<=127;i++) { + char c = (char)i; + upper_char_map[i] = lower_char_map[i] = c; + if (isupper(c)) lower_char_map[c] = tolower(c); + if (islower(c)) upper_char_map[c] = toupper(c); + } + + /* valid for all DOS PC */ + add_dos_char(142,0); /* A trema */ + add_dos_char(143,0); /* A o */ + add_dos_char(144,0); /* E ' */ + add_dos_char(146,0); /* AE */ + add_dos_char(153,0); /* O trema */ + add_dos_char(154,0); /* U trema */ + add_dos_char(165,0); /* N tilda */ + add_dos_char(128,0); /* C cedille */ + add_dos_char(156,0); /* Pound */ + add_dos_char(183,0); /* A ` (WIN)*/ + add_dos_char(157,0); /* Phi (WIN)*/ + add_dos_char(212,0); /* E` (WIN)*/ +} + + +/******************************************************************* +add characters depending on a string passed by the user +********************************************************************/ +void add_char_string(char *s) +{ + char *extra_chars = (char *)strdup(s); + char *t; + if (!extra_chars) return; + + for (t=strtok(extra_chars," \t\r\n"); t; t=strtok(NULL," \t\r\n")) { + char c1=0,c2=0; + int i1=0,i2=0; + if (isdigit(*t) || (*t)=='-') { + sscanf(t,"%i:%i",&i1,&i2); + add_dos_char(i1,i2); + } else { + sscanf(t,"%c:%c",&c1,&c2); + add_dos_char(c1,c2); + } + } + + free(extra_chars); +} diff --git a/source3/lib/fault.c b/source3/lib/fault.c new file mode 100644 index 0000000000..20c75f7876 --- /dev/null +++ b/source3/lib/fault.c @@ -0,0 +1,86 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Critical Fault handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#ifdef LINUX +#define __KERNEL__ +#endif + +#include "includes.h" +extern int DEBUGLEVEL; + + +static void (*cont_fn)(); + + +/******************************************************************* +report a fault +********************************************************************/ +static void fault_report(int sig) +{ + DEBUG(0,("===============================================================\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION)); + DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n")); + DEBUG(0,("===============================================================\n")); + +#if AJT + ajt_panic(); +#endif + + if (cont_fn) + { + fault_setup(cont_fn); + cont_fn(NULL); +#ifdef SIGSEGV + signal(SIGSEGV,SIGNAL_CAST SIG_DFL); +#endif +#ifdef SIGBUS + signal(SIGBUS,SIGNAL_CAST SIG_DFL); +#endif + return; /* this should cause a core dump */ + } + exit(1); +} + +/**************************************************************************** +catch serious errors +****************************************************************************/ +static void sig_fault(int sig) +{ + fault_report(sig); +} + +/******************************************************************* +setup our fault handlers +********************************************************************/ +void fault_setup(void (*fn)()) +{ + cont_fn = fn; + +#ifdef SIGSEGV + signal(SIGSEGV,SIGNAL_CAST sig_fault); +#endif +#ifdef SIGBUS + signal(SIGBUS,SIGNAL_CAST sig_fault); +#endif +} + + + diff --git a/source3/lib/getsmbpass.c b/source3/lib/getsmbpass.c new file mode 100644 index 0000000000..07a7dbfd9b --- /dev/null +++ b/source3/lib/getsmbpass.c @@ -0,0 +1,166 @@ +/* Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* Modified to use with samba by Jeremy Allison, 8th July 1995. */ + +#include "includes.h" + +#ifdef REPLACE_GETPASS + +#ifdef SYSV_TERMIO + +/* SYSTEM V TERMIO HANDLING */ + +static struct termio t; + +#define ECHO_IS_ON(t) ((t).c_lflag & ECHO) +#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO) + +#ifndef TCSAFLUSH +#define TCSAFLUSH 1 +#endif + +#ifndef TCSANOW +#define TCSANOW 0 +#endif + +int tcgetattr(int fd, struct termio *t) +{ + return ioctl(fd, TCGETA, t); +} + +int tcsetattr(int fd, int flags, const struct termio *t) +{ + if(flags & TCSAFLUSH) + ioctl(fd, TCFLSH, TCIOFLUSH); + return ioctl(fd, TCSETS, t); +} + +#else /* SYSV_TERMIO */ +#ifdef BSD_TERMIO + +/* BSD TERMIO HANDLING */ + +static struct sgttyb t; + +#define ECHO_IS_ON(t) ((t).sg_flags & ECHO) +#define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO) + +#ifndef TCSAFLUSH +#define TCSAFLUSH 1 +#endif + +#ifndef TCSANOW +#define TCSANOW 0 +#endif + +int tcgetattr(int fd, struct sgttyb *t) +{ + return ioctl(fd, TIOCGETP, (char *)t); +} + +int tcsetattr(int fd, int flags, const struct sgttyb *t) +{ + return ioctl(fd, TIOCSETP, (char *)t); +} + +#else /* BSD_TERMIO */ + +/* POSIX TERMIO HANDLING */ +#define ECHO_IS_ON(t) ((t).c_lflag & ECHO) +#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO) + +static struct termios t; +#endif /* BSD_TERMIO */ +#endif /* SYSV_TERMIO */ + +char * +getsmbpass(char *prompt) +{ + FILE *in, *out; + int echo_off; + static char buf[256]; + static size_t bufsize = sizeof(buf); + size_t nread; + + /* Catch problematic signals */ + signal(SIGINT, SIGNAL_CAST SIG_IGN); + + /* Try to write to and read from the terminal if we can. + If we can't open the terminal, use stderr and stdin. */ + + in = fopen ("/dev/tty", "w+"); + if (in == NULL) + { + in = stdin; + out = stderr; + } + else + out = in; + + setvbuf(in, NULL, _IONBF, 0); + + /* Turn echoing off if it is on now. */ + + if (tcgetattr (fileno (in), &t) == 0) + { + if (ECHO_IS_ON(t)) + { + TURN_ECHO_OFF(t); + echo_off = tcsetattr (fileno (in), TCSAFLUSH, &t) == 0; + TURN_ECHO_ON(t); + } + else + echo_off = 0; + } + else + echo_off = 0; + + /* Write the prompt. */ + fputs (prompt, out); + fflush (out); + + /* Read the password. */ + buf[0] = 0; + fgets(buf, bufsize, in); + nread = strlen(buf); + if (buf[nread - 1] == '\n') + buf[nread - 1] = '\0'; + + /* Restore echoing. */ + if (echo_off) + (void) tcsetattr (fileno (in), TCSANOW, &t); + + if (in != stdin) + /* We opened the terminal; now close it. */ + fclose (in); + + /* Catch problematic signals */ + signal(SIGINT, SIGNAL_CAST SIG_DFL); + + printf("\n"); + return buf; +} + +#else + +void getsmbpasswd_dummy() {;} +#endif diff --git a/source3/lib/kanji.c b/source3/lib/kanji.c new file mode 100644 index 0000000000..0af476eb15 --- /dev/null +++ b/source3/lib/kanji.c @@ -0,0 +1,895 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Kanji Extensions + Copyright (C) Andrew Tridgell 1992-1994 + + 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. + + Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5 + and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11 + and add all jis codes sequence type at 1995.8.16 + Notes: Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp> +*/ +#ifdef KANJI + +#define _KANJI_C_ +#include "includes.h" + +/* coding system keep in */ +int coding_system = SJIS_CODE; + +/* jis si/so sequence */ +char jis_kso = JIS_KSO; +char jis_ksi = JIS_KSI; +char hex_tag = HEXTAG; + +/******************************************************************* + SHIFT JIS functions +********************************************************************/ +/******************************************************************* + search token from S1 separated any char of S2 + S1 contain SHIFT JIS chars. +********************************************************************/ +char * +sj_strtok (char *s1, const char *s2) +{ + static char *s = NULL; + char *q; + if (!s1) { + if (!s) { + return NULL; + } + s1 = s; + } + for (q = s1; *s1; ) { + if (is_shift_jis (*s1)) { + s1 += 2; + } else if (is_kana (*s1)) { + s1++; + } else { + char *p = strchr (s2, *s1); + if (p) { + if (s1 != q) { + s = s1 + 1; + *s1 = '\0'; + return q; + } + q = s1 + 1; + } + s1++; + } + } + s = NULL; + if (*q) { + return q; + } + return NULL; +} + +/******************************************************************* + search string S2 from S1 + S1 contain SHIFT JIS chars. +********************************************************************/ +char * +sj_strstr (const char *s1, const char *s2) +{ + register int len = strlen ((char *) s2); + if (!*s2) + return (char *) s1; + for (;*s1;) { + if (*s1 == *s2) { + if (strncmp (s1, s2, len) == 0) + return (char *) s1; + } + if (is_shift_jis (*s1)) { + s1 += 2; + } else { + s1++; + } + } + return 0; +} + +/******************************************************************* + Search char C from beginning of S. + S contain SHIFT JIS chars. +********************************************************************/ +char * +sj_strchr (const char *s, int c) +{ + for (; *s; ) { + if (*s == c) + return (char *) s; + if (is_shift_jis (*s)) { + s += 2; + } else { + s++; + } + } + return 0; +} + +/******************************************************************* + Search char C end of S. + S contain SHIFT JIS chars. +********************************************************************/ +char * +sj_strrchr (const char *s, int c) +{ + register char *q; + + for (q = 0; *s; ) { + if (*s == c) { + q = (char *) s; + } + if (is_shift_jis (*s)) { + s += 2; + } else { + s++; + } + } + return q; +} + +/******************************************************************* + Code conversion +********************************************************************/ +/* convesion buffer */ +static char cvtbuf[1024]; + +/******************************************************************* + EUC <-> SJIS +********************************************************************/ +static int +euc2sjis (register int hi, register int lo) +{ + if (hi & 1) + return ((hi / 2 + (hi < 0xdf ? 0x31 : 0x71)) << 8) | + (lo - (lo >= 0xe0 ? 0x60 : 0x61)); + else + return ((hi / 2 + (hi < 0xdf ? 0x30 : 0x70)) << 8) | (lo - 2); +} + +static int +sjis2euc (register int hi, register int lo) +{ + if (lo >= 0x9f) + return ((hi * 2 - (hi >= 0xe0 ? 0xe0 : 0x60)) << 8) | (lo + 2); + else + return ((hi * 2 - (hi >= 0xe0 ? 0xe1 : 0x61)) << 8) | + (lo + (lo >= 0x7f ? 0x60 : 0x61)); +} + +/******************************************************************* + Convert FROM contain SHIFT JIS codes to EUC codes + return converted buffer +********************************************************************/ +static char * +sj_to_euc (const char *from, BOOL overwrite) +{ + register char *out; + char *save; + + save = (char *) from; + for (out = cvtbuf; *from;) { + if (is_shift_jis (*from)) { + int code = sjis2euc ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } else if (is_kana (*from)) { + *out++ = euc_kana; + *out++ = *from++; + } else { + *out++ = *from++; + } + } + *out = 0; + if (overwrite) { + strcpy((char *) save, (char *) cvtbuf); + return (char *) save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain EUC codes to SHIFT JIS codes + return converted buffer +********************************************************************/ +static char * +euc_to_sj (const char *from, BOOL overwrite) +{ + register char *out; + char *save; + + save = (char *) from; + for (out = cvtbuf; *from; ) { + if (is_euc (*from)) { + int code = euc2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } else if (is_euc_kana (*from)) { + *out++ = from[1]; + from += 2; + } else { + *out++ = *from++; + } + } + *out = 0; + if (overwrite) { + strcpy(save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + JIS7,JIS8,JUNET <-> SJIS +********************************************************************/ +static int +sjis2jis (register int hi, register int lo) +{ + if (lo >= 0x9f) + return ((hi * 2 - (hi >= 0xe0 ? 0x160 : 0xe0)) << 8) | (lo - 0x7e); + else + return ((hi * 2 - (hi >= 0xe0 ? 0x161 : 0xe1)) << 8) | + (lo - (lo >= 0x7f ? 0x20 : 0x1f)); +} + +static int +jis2sjis (register int hi, register int lo) +{ + if (hi & 1) + return ((hi / 2 + (hi < 0x5f ? 0x71 : 0xb1)) << 8) | + (lo + (lo >= 0x60 ? 0x20 : 0x1f)); + else + return ((hi / 2 + (hi < 0x5f ? 0x70 : 0xb0)) << 8) | (lo + 0x7e); +} + +/******************************************************************* + Convert FROM contain JIS codes to SHIFT JIS codes + return converted buffer +********************************************************************/ +static char * +jis8_to_sj (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from;) { + if (is_esc (*from)) { + if (is_so1 (from[1]) && is_so2 (from[2])) { + shifted = _KJ_KANJI; + from += 3; + } else if (is_si1 (from[1]) && is_si2 (from[2])) { + shifted = _KJ_ROMAN; + from += 3; + } else { /* sequence error */ + goto normal; + } + } else { + normal: + switch (shifted) { + default: + case _KJ_ROMAN: + *out++ = *from++; + break; + case _KJ_KANJI: + { + int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } + break; + } + } + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain SHIFT JIS codes to JIS codes + return converted buffer +********************************************************************/ +static char * +sj_to_jis8 (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from; ) { + if (is_shift_jis (*from)) { + int code; + switch (shifted) { + case _KJ_ROMAN: /* to KANJI */ + *out++ = jis_esc; + *out++ = jis_so1; + *out++ = jis_kso; + shifted = _KJ_KANJI; + break; + } + code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } else { + switch (shifted) { + case _KJ_KANJI: /* to ROMAN/KANA */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_ksi; + shifted = _KJ_ROMAN; + break; + } + *out++ = *from++; + } + } + switch (shifted) { + case _KJ_KANJI: /* to ROMAN/KANA */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_ksi; + shifted = _KJ_ROMAN; + break; + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain 7 bits JIS codes to SHIFT JIS codes + return converted buffer +********************************************************************/ +static char * +jis7_to_sj (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from;) { + if (is_esc (*from)) { + if (is_so1 (from[1]) && is_so2 (from[2])) { + shifted = _KJ_KANJI; + from += 3; + } else if (is_si1 (from[1]) && is_si2 (from[2])) { + shifted = _KJ_ROMAN; + from += 3; + } else { /* sequence error */ + goto normal; + } + } else if (is_so (*from)) { + shifted = _KJ_KANA; /* to KANA */ + from++; + } else if (is_si (*from)) { + shifted = _KJ_ROMAN; /* to ROMAN */ + from++; + } else { + normal: + switch (shifted) { + default: + case _KJ_ROMAN: + *out++ = *from++; + break; + case _KJ_KANJI: + { + int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } + break; + case _KJ_KANA: + *out++ = ((int) from[0]) + 0x80; + break; + } + } + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain SHIFT JIS codes to 7 bits JIS codes + return converted buffer +********************************************************************/ +static char * +sj_to_jis7 (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from; ) { + if (is_shift_jis (*from)) { + int code; + switch (shifted) { + case _KJ_KANA: + *out++ = jis_si; /* to ROMAN and through down */ + case _KJ_ROMAN: /* to KANJI */ + *out++ = jis_esc; + *out++ = jis_so1; + *out++ = jis_kso; + shifted = _KJ_KANJI; + break; + } + code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } else if (is_kana (from[0])) { + switch (shifted) { + case _KJ_KANJI: /* to ROMAN */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_ksi; + case _KJ_ROMAN: /* to KANA */ + *out++ = jis_so; + shifted = _KJ_KANA; + break; + } + *out++ = ((int) *from++) - 0x80; + } else { + switch (shifted) { + case _KJ_KANA: + *out++ = jis_si; /* to ROMAN */ + shifted = _KJ_ROMAN; + break; + case _KJ_KANJI: /* to ROMAN */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_ksi; + shifted = _KJ_ROMAN; + break; + } + *out++ = *from++; + } + } + switch (shifted) { + case _KJ_KANA: + *out++ = jis_si; /* to ROMAN */ + break; + case _KJ_KANJI: /* to ROMAN */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_ksi; + break; + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain 7 bits JIS(junet) codes to SHIFT JIS codes + return converted buffer +********************************************************************/ +static char * +junet_to_sj (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from;) { + if (is_esc (*from)) { + if (is_so1 (from[1]) && is_so2 (from[2])) { + shifted = _KJ_KANJI; + from += 3; + } else if (is_si1 (from[1]) && is_si2 (from[2])) { + shifted = _KJ_ROMAN; + from += 3; + } else if (is_juk1(from[1]) && is_juk2 (from[2])) { + shifted = _KJ_KANA; + from += 3; + } else { /* sequence error */ + goto normal; + } + } else { + normal: + switch (shifted) { + default: + case _KJ_ROMAN: + *out++ = *from++; + break; + case _KJ_KANJI: + { + int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } + break; + case _KJ_KANA: + *out++ = ((int) from[0]) + 0x80; + break; + } + } + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + Convert FROM contain SHIFT JIS codes to 7 bits JIS(junet) codes + return converted buffer +********************************************************************/ +static char * +sj_to_junet (const char *from, BOOL overwrite) +{ + register char *out; + register int shifted; + char *save; + + shifted = _KJ_ROMAN; + save = (char *) from; + for (out = cvtbuf; *from; ) { + if (is_shift_jis (*from)) { + int code; + switch (shifted) { + case _KJ_KANA: + case _KJ_ROMAN: /* to KANJI */ + *out++ = jis_esc; + *out++ = jis_so1; + *out++ = jis_so2; + shifted = _KJ_KANJI; + break; + } + code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); + *out++ = (code >> 8) & 0xff; + *out++ = code; + from += 2; + } else if (is_kana (from[0])) { + switch (shifted) { + case _KJ_KANJI: /* to ROMAN */ + case _KJ_ROMAN: /* to KANA */ + *out++ = jis_esc; + *out++ = junet_kana1; + *out++ = junet_kana2; + shifted = _KJ_KANA; + break; + } + *out++ = ((int) *from++) - 0x80; + } else { + switch (shifted) { + case _KJ_KANA: + case _KJ_KANJI: /* to ROMAN */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_si2; + shifted = _KJ_ROMAN; + break; + } + *out++ = *from++; + } + } + switch (shifted) { + case _KJ_KANA: + case _KJ_KANJI: /* to ROMAN */ + *out++ = jis_esc; + *out++ = jis_si1; + *out++ = jis_si2; + break; + } + *out = 0; + if (overwrite) { + strcpy (save, (char *) cvtbuf); + return save; + } else { + return cvtbuf; + } +} + +/******************************************************************* + HEX <-> SJIS +********************************************************************/ +/* ":xx" -> a byte */ +static char * +hex_to_sj (const char *from, BOOL overwrite) +{ + char *sp, *dp; + + sp = (char *) from; + dp = cvtbuf; + while (*sp) { + if (*sp == hex_tag && isxdigit (sp[1]) && isxdigit (sp[2])) { + *dp++ = (hex2bin (sp[1])<<4) | (hex2bin (sp[2])); + sp += 3; + } else + *dp++ = *sp++; + } + *dp = '\0'; + if (overwrite) { + strcpy ((char *) from, (char *) cvtbuf); + return (char *) from; + } else { + return cvtbuf; + } +} + +/******************************************************************* + kanji/kana -> ":xx" +********************************************************************/ +static char * +sj_to_hex (const char *from, BOOL overwrite) +{ + unsigned char *sp, *dp; + + sp = (unsigned char*) from; + dp = (unsigned char*) cvtbuf; + while (*sp) { + if (is_kana(*sp)) { + *dp++ = hex_tag; + *dp++ = bin2hex (((*sp)>>4)&0x0f); + *dp++ = bin2hex ((*sp)&0x0f); + sp++; + } else if (is_shift_jis (*sp) && is_shift_jis2 (sp[1])) { + *dp++ = hex_tag; + *dp++ = bin2hex (((*sp)>>4)&0x0f); + *dp++ = bin2hex ((*sp)&0x0f); + sp++; + *dp++ = hex_tag; + *dp++ = bin2hex (((*sp)>>4)&0x0f); + *dp++ = bin2hex ((*sp)&0x0f); + sp++; + } else + *dp++ = *sp++; + } + *dp = '\0'; + if (overwrite) { + strcpy ((char *) from, (char *) cvtbuf); + return (char *) from; + } else { + return cvtbuf; + } +} + +/******************************************************************* + kanji/kana -> ":xx" +********************************************************************/ +static char * +sj_to_cap (const char *from, BOOL overwrite) +{ + unsigned char *sp, *dp; + + sp = (unsigned char*) from; + dp = (unsigned char*) cvtbuf; + while (*sp) { + if (*sp >= 0x80) { + *dp++ = hex_tag; + *dp++ = bin2hex (((*sp)>>4)&0x0f); + *dp++ = bin2hex ((*sp)&0x0f); + sp++; + } else { + *dp++ = *sp++; + } + } + *dp = '\0'; + if (overwrite) { + strcpy ((char *) from, (char *) cvtbuf); + return (char *) from; + } else { + return cvtbuf; + } +} + +/******************************************************************* + sj to sj +********************************************************************/ +static char * +sj_to_sj (const char *from, BOOL overwrite) +{ + if (!overwrite) { + strcpy (cvtbuf, (char *) from); + return cvtbuf; + } else { + return (char *) from; + } +} + +/************************************************************************ + conversion: + _dos_to_unix _unix_to_dos +************************************************************************/ + +char* (*_dos_to_unix) (const char *str, BOOL overwrite) = sj_to_sj; +char* (*_unix_to_dos) (const char *str, BOOL overwrite) = sj_to_sj; + +static int +setup_string_function (int codes) +{ + switch (codes) { + default: + case SJIS_CODE: + _dos_to_unix = sj_to_sj; + _unix_to_dos = sj_to_sj; + + break; + + case EUC_CODE: + _dos_to_unix = sj_to_euc; + _unix_to_dos = euc_to_sj; + break; + + case JIS7_CODE: + _dos_to_unix = sj_to_jis7; + _unix_to_dos = jis7_to_sj; + break; + + case JIS8_CODE: + _dos_to_unix = sj_to_jis8; + _unix_to_dos = jis8_to_sj; + break; + + case JUNET_CODE: + _dos_to_unix = sj_to_junet; + _unix_to_dos = junet_to_sj; + break; + + case HEX_CODE: + _dos_to_unix = sj_to_hex; + _unix_to_dos = hex_to_sj; + break; + + case CAP_CODE: + _dos_to_unix = sj_to_cap; + _unix_to_dos = hex_to_sj; + break; + } + return codes; +} + +/* + * Interpret coding system. + */ +int +interpret_coding_system (char *str, int def) +{ + int codes = def; + + if (strequal (str, "sjis")) { + codes = SJIS_CODE; + } else if (strequal (str, "euc")) { + codes = EUC_CODE; + } else if (strequal (str, "cap")) { + codes = CAP_CODE; + hex_tag = HEXTAG; + } else if (strequal (str, "hex")) { + codes = HEX_CODE; + hex_tag = HEXTAG; + } else if (strncasecmp (str, "hex", 3)) { + codes = HEX_CODE; + hex_tag = (str[3] ? str[3] : HEXTAG); + } else if (strequal (str, "j8bb")) { + codes = JIS8_CODE; + jis_kso = 'B'; + jis_ksi = 'B'; + } else if (strequal (str, "j8bj") || strequal (str, "jis8")) { + codes = JIS8_CODE; + jis_kso = 'B'; + jis_ksi = 'J'; + } else if (strequal (str, "j8bh")) { + codes = JIS8_CODE; + jis_kso = 'B'; + jis_ksi = 'H'; + } else if (strequal (str, "j8@b")) { + codes = JIS8_CODE; + jis_kso = '@'; + jis_ksi = 'B'; + } else if (strequal (str, "j8@j")) { + codes = JIS8_CODE; + jis_kso = '@'; + jis_ksi = 'J'; + } else if (strequal (str, "j8@h")) { + codes = JIS8_CODE; + jis_kso = '@'; + jis_ksi = 'H'; + } else if (strequal (str, "j7bb")) { + codes = JIS7_CODE; + jis_kso = 'B'; + jis_ksi = 'B'; + } else if (strequal (str, "j7bj") || strequal (str, "jis7")) { + codes = JIS7_CODE; + jis_kso = 'B'; + jis_ksi = 'J'; + } else if (strequal (str, "j7bh")) { + codes = JIS7_CODE; + jis_kso = 'B'; + jis_ksi = 'H'; + } else if (strequal (str, "j7@b")) { + codes = JIS7_CODE; + jis_kso = '@'; + jis_ksi = 'B'; + } else if (strequal (str, "j7@j")) { + codes = JIS7_CODE; + jis_kso = '@'; + jis_ksi = 'J'; + } else if (strequal (str, "j7@h")) { + codes = JIS7_CODE; + jis_kso = '@'; + jis_ksi = 'H'; + } else if (strequal (str, "jubb")) { + codes = JUNET_CODE; + jis_kso = 'B'; + jis_ksi = 'B'; + } else if (strequal (str, "jubj") || strequal (str, "junet")) { + codes = JUNET_CODE; + jis_kso = 'B'; + jis_ksi = 'J'; + } else if (strequal (str, "jubh")) { + codes = JUNET_CODE; + jis_kso = 'B'; + jis_ksi = 'H'; + } else if (strequal (str, "ju@b")) { + codes = JUNET_CODE; + jis_kso = '@'; + jis_ksi = 'B'; + } else if (strequal (str, "ju@j")) { + codes = JUNET_CODE; + jis_kso = '@'; + jis_ksi = 'J'; + } else if (strequal (str, "ju@h")) { + codes = JUNET_CODE; + jis_kso = '@'; + jis_ksi = 'H'; + } + return setup_string_function (codes); +} +#else +int kanji_dummy_procedure(void) +{return 0;} +#endif /* KANJI */ diff --git a/source3/lib/md4.c b/source3/lib/md4.c new file mode 100644 index 0000000000..485e231a78 --- /dev/null +++ b/source3/lib/md4.c @@ -0,0 +1,299 @@ +#ifdef SMB_PASSWD +/* + This code is from rfc1186. +*/ + + /* + ** ******************************************************************** + ** md4.c -- Implementation of MD4 Message Digest Algorithm ** + ** Updated: 2/16/90 by Ronald L. Rivest ** + ** (C) 1990 RSA Data Security, Inc. ** + ** ******************************************************************** + */ + + /* + ** To use MD4: + ** -- Include md4.h in your program + ** -- Declare an MDstruct MD to hold the state of the digest + ** computation. + ** -- Initialize MD using MDbegin(&MD) + ** -- For each full block (64 bytes) X you wish to process, call + ** MDupdate(&MD,X,512) + ** (512 is the number of bits in a full block.) + ** -- For the last block (less than 64 bytes) you wish to process, + ** MDupdate(&MD,X,n) + ** where n is the number of bits in the partial block. A partial + ** block terminates the computation, so every MD computation + ** should terminate by processing a partial block, even if it + ** has n = 0. + ** -- The message digest is available in MD.buffer[0] ... + ** MD.buffer[3]. (Least-significant byte of each word + ** should be output first.) + ** -- You can print out the digest using MDprint(&MD) + */ + + /* Implementation notes: + ** This implementation assumes that ints are 32-bit quantities. + ** If the machine stores the least-significant byte of an int in the + ** least-addressed byte (e.g., VAX and 8086), then LOWBYTEFIRST + ** should be set to TRUE. Otherwise (e.g., SUNS), LOWBYTEFIRST + ** should be set to FALSE. Note that on machines with LOWBYTEFIRST + ** FALSE the routine MDupdate modifies has a side-effect on its input + ** array (the order of bytes in each word are reversed). If this is + ** undesired a call to MDreverse(X) can reverse the bytes of X back + ** into order after each call to MDupdate. + */ + +#define TRUE 1 +#define FALSE 0 + + /* Compile-time includes + */ + +#include <stdio.h> +#include "md4.h" + +#define uchar unsigned char +#define int16 unsigned short +#define uint32 unsigned int + +#include "byteorder.h" + + /* Compile-time declarations of MD4 "magic constants". + */ +#define I0 0x67452301 /* Initial values for MD buffer */ +#define I1 0xefcdab89 +#define I2 0x98badcfe +#define I3 0x10325476 +#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */ +#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */ + /* C2 and C3 are from Knuth, The Art of Programming, Volume 2 + ** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley. + ** Table 2, page 660. + */ + +#define fs1 3 /* round 1 shift amounts */ +#define fs2 7 +#define fs3 11 +#define fs4 19 +#define gs1 3 /* round 2 shift amounts */ +#define gs2 5 +#define gs3 9 +#define gs4 13 +#define hs1 3 /* round 3 shift amounts */ +#define hs2 9 +#define hs3 11 +#define hs4 15 + + /* Compile-time macro declarations for MD4. + ** Note: The "rot" operator uses the variable "tmp". + ** It assumes tmp is declared as unsigned int, so that the >> + ** operator will shift in zeros rather than extending the sign bit. + */ +#define f(X,Y,Z) ((X&Y) | ((~X)&Z)) +#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z)) +#define h(X,Y,Z) (X^Y^Z) +#define rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S))) +#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s) +#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s) +#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s) + + /* MDprint(MDp) + ** Print message digest buffer MDp as 32 hexadecimal digits. + ** Order is from low-order byte of buffer[0] to high-order byte of + ** buffer[3]. + ** Each byte is printed with high-order hexadecimal digit first. + ** This is a user-callable routine. + */ + void + MDprint(MDp) + MDptr MDp; + { int i,j; + for (i=0;i<4;i++) + for (j=0;j<32;j=j+8) + printf("%02x",(MDp->buffer[i]>>j) & 0xFF); + } + + /* MDbegin(MDp) + ** Initialize message digest buffer MDp. + ** This is a user-callable routine. + */ + void + MDbegin(MDp) + MDptr MDp; + { int i; + MDp->buffer[0] = I0; + MDp->buffer[1] = I1; + MDp->buffer[2] = I2; + MDp->buffer[3] = I3; + for (i=0;i<8;i++) MDp->count[i] = 0; + MDp->done = 0; + } + + /* MDreverse(X) + ** Reverse the byte-ordering of every int in X. + ** Assumes X is an array of 16 ints. + ** The macro revx reverses the byte-ordering of the next word of X. + */ + void MDreverse(X) + unsigned int *X; + { register unsigned int t; + register unsigned int i; + + for(i = 0; i < 16; i++) { + t = X[i]; + SIVAL(X,i*4,t); + } + } + + /* MDblock(MDp,X) + ** Update message digest buffer MDp->buffer using 16-word data block X. + ** Assumes all 16 words of X are full of data. + ** Does not update MDp->count. + ** This routine is not user-callable. + */ + static void + MDblock(MDp,X) + MDptr MDp; + unsigned int *X; + { + register unsigned int tmp, A, B, C, D; + MDreverse(X); + A = MDp->buffer[0]; + B = MDp->buffer[1]; + C = MDp->buffer[2]; + D = MDp->buffer[3]; + /* Update the message digest buffer */ + ff(A , B , C , D , 0 , fs1); /* Round 1 */ + ff(D , A , B , C , 1 , fs2); + ff(C , D , A , B , 2 , fs3); + ff(B , C , D , A , 3 , fs4); + ff(A , B , C , D , 4 , fs1); + ff(D , A , B , C , 5 , fs2); + ff(C , D , A , B , 6 , fs3); + ff(B , C , D , A , 7 , fs4); + ff(A , B , C , D , 8 , fs1); + ff(D , A , B , C , 9 , fs2); + ff(C , D , A , B , 10 , fs3); + ff(B , C , D , A , 11 , fs4); + ff(A , B , C , D , 12 , fs1); + ff(D , A , B , C , 13 , fs2); + ff(C , D , A , B , 14 , fs3); + ff(B , C , D , A , 15 , fs4); + gg(A , B , C , D , 0 , gs1); /* Round 2 */ + gg(D , A , B , C , 4 , gs2); + gg(C , D , A , B , 8 , gs3); + gg(B , C , D , A , 12 , gs4); + gg(A , B , C , D , 1 , gs1); + gg(D , A , B , C , 5 , gs2); + gg(C , D , A , B , 9 , gs3); + gg(B , C , D , A , 13 , gs4); + gg(A , B , C , D , 2 , gs1); + gg(D , A , B , C , 6 , gs2); + gg(C , D , A , B , 10 , gs3); + gg(B , C , D , A , 14 , gs4); + gg(A , B , C , D , 3 , gs1); + gg(D , A , B , C , 7 , gs2); + gg(C , D , A , B , 11 , gs3); + gg(B , C , D , A , 15 , gs4); + hh(A , B , C , D , 0 , hs1); /* Round 3 */ + hh(D , A , B , C , 8 , hs2); + hh(C , D , A , B , 4 , hs3); + hh(B , C , D , A , 12 , hs4); + hh(A , B , C , D , 2 , hs1); + hh(D , A , B , C , 10 , hs2); + hh(C , D , A , B , 6 , hs3); + hh(B , C , D , A , 14 , hs4); + hh(A , B , C , D , 1 , hs1); + hh(D , A , B , C , 9 , hs2); + hh(C , D , A , B , 5 , hs3); + hh(B , C , D , A , 13 , hs4); + hh(A , B , C , D , 3 , hs1); + hh(D , A , B , C , 11 , hs2); + hh(C , D , A , B , 7 , hs3); + hh(B , C , D , A , 15 , hs4); + MDp->buffer[0] += A; + MDp->buffer[1] += B; + MDp->buffer[2] += C; + MDp->buffer[3] += D; + } + + /* MDupdate(MDp,X,count) + ** Input: MDp -- an MDptr + ** X -- a pointer to an array of unsigned characters. + ** count -- the number of bits of X to use. + ** (if not a multiple of 8, uses high bits of last byte.) + ** Update MDp using the number of bits of X given by count. + ** This is the basic input routine for an MD4 user. + ** The routine completes the MD computation when count < 512, so + ** every MD computation should end with one call to MDupdate with a + ** count less than 512. A call with count 0 will be ignored if the + ** MD has already been terminated (done != 0), so an extra call with + ** count 0 can be given as a "courtesy close" to force termination + ** if desired. + */ + void + MDupdate(MDp,X,count) + MDptr MDp; + unsigned char *X; + unsigned int count; + { unsigned int i, tmp, bit, byte, mask; + unsigned char XX[64]; + unsigned char *p; + /* return with no error if this is a courtesy close with count + ** zero and MDp->done is true. + */ + if (count == 0 && MDp->done) return; + /* check to see if MD is already done and report error */ + if (MDp->done) + { printf("\nError: MDupdate MD already done."); return; } + /* Add count to MDp->count */ + tmp = count; + p = MDp->count; + while (tmp) + { tmp += *p; + *p++ = tmp; + tmp = tmp >> 8; + } + /* Process data */ + if (count == 512) + { /* Full block of data to handle */ + MDblock(MDp,(unsigned int *)X); + } + else if (count > 512) /* Check for count too large */ + { printf("\nError: MDupdate called with illegal count value %d." + ,count); + return; + } + else /* partial block -- must be last block so finish up */ + { /* Find out how many bytes and residual bits there are */ + byte = count >> 3; + bit = count & 7; + /* Copy X into XX since we need to modify it */ + for (i=0;i<=byte;i++) XX[i] = X[i]; + for (i=byte+1;i<64;i++) XX[i] = 0; + /* Add padding '1' bit and low-order zeros in last byte */ + mask = 1 << (7 - bit); + XX[byte] = (XX[byte] | mask) & ~( mask - 1); + /* If room for bit count, finish up with this block */ + if (byte <= 55) + { for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; + MDblock(MDp,(unsigned int *)XX); + } + else /* need to do two blocks to finish up */ + { MDblock(MDp,(unsigned int *)XX); + for (i=0;i<56;i++) XX[i] = 0; + for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; + MDblock(MDp,(unsigned int *)XX); + } + /* Set flag saying we're done with MD computation */ + MDp->done = 1; + } + } + + /* + ** End of md4.c + */ +#else +void md4_dummy() {;} +#endif diff --git a/source3/lib/system.c b/source3/lib/system.c new file mode 100644 index 0000000000..938746e9c9 --- /dev/null +++ b/source3/lib/system.c @@ -0,0 +1,222 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Samba system utilities + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" + +extern int DEBUGLEVEL; + +/* + The idea is that this file will eventually have wrappers around all + important system calls in samba. The aim is twofold: + + - to enable easier porting by putting OS dependent stuff in here + + - to allow for hooks into other "pseudo-filesystems" + + - to allow easier integration of things like the japanese extensions +*/ + + +/******************************************************************* +this replaces the normal select() system call +return if some data has arrived on one of the file descriptors +return -1 means error +********************************************************************/ +#ifdef NO_SELECT +static int pollfd(int fd) +{ + int r=0; + +#ifdef HAS_RDCHK + r = rdchk(fd); +#elif defined(TCRDCHK) + (void)ioctl(fd, TCRDCHK, &r); +#else + (void)ioctl(fd, FIONREAD, &r); +#endif + + return(r); +} + +int sys_select(fd_set *fds,struct timeval *tval) +{ + fd_set fds2; + int counter=0; + int found=0; + + FD_ZERO(&fds2); + + while (1) + { + int i; + for (i=0;i<255;i++) { + if (FD_ISSET(i,fds) && pollfd(i)>0) { + found++; + FD_SET(i,&fds2); + } + } + + if (found) { + memcpy((void *)fds,(void *)&fds2,sizeof(fds2)); + return(found); + } + + if (tval && tval.tv_sec < counter) return(0); + sleep(1); + counter++; + } +} + +#else +int sys_select(fd_set *fds,struct timeval *tval) +{ + struct timeval t2; + int selrtn; + + do { + if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2)); + errno = 0; + selrtn = select(16,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL); + } while (selrtn<0 && errno == EINTR); + + return(selrtn); +} +#endif + + +/******************************************************************* +just a unlink wrapper +********************************************************************/ +int sys_unlink(char *fname) +{ + return(unlink(dos_to_unix(fname,False))); +} + + +/******************************************************************* +a simple open() wrapper +********************************************************************/ +int sys_open(char *fname,int flags,int mode) +{ + return(open(dos_to_unix(fname,False),flags,mode)); +} + + +/******************************************************************* +a simple opendir() wrapper +********************************************************************/ +DIR *sys_opendir(char *dname) +{ + return(opendir(dos_to_unix(dname,False))); +} + + +/******************************************************************* +and a stat() wrapper +********************************************************************/ +int sys_stat(char *fname,struct stat *sbuf) +{ + return(stat(dos_to_unix(fname,False),sbuf)); +} + +/******************************************************************* +don't forget lstat() +********************************************************************/ +int sys_lstat(char *fname,struct stat *sbuf) +{ + return(lstat(dos_to_unix(fname,False),sbuf)); +} + + +/******************************************************************* +mkdir() gets a wrapper +********************************************************************/ +int sys_mkdir(char *dname,int mode) +{ + return(mkdir(dos_to_unix(dname,False),mode)); +} + + +/******************************************************************* +do does rmdir() +********************************************************************/ +int sys_rmdir(char *dname) +{ + return(rmdir(dos_to_unix(dname,False))); +} + + +/******************************************************************* +I almost forgot chdir() +********************************************************************/ +int sys_chdir(char *dname) +{ + return(chdir(dos_to_unix(dname,False))); +} + + +/******************************************************************* +now for utime() +********************************************************************/ +int sys_utime(char *fname,struct utimbuf *times) +{ + return(utime(dos_to_unix(fname,False),times)); +} + +/******************************************************************* +for rename() +********************************************************************/ +int sys_rename(char *from, char *to) +{ +#ifdef KANJI + pstring zfrom, zto; + strcpy (zfrom, dos_to_unix (from, False)); + strcpy (zto, dos_to_unix (to, False)); + return rename (zfrom, zto); +#else + return rename (from, to); +#endif /* KANJI */ +} + + +/******************************************************************* +chown isn't used much but OS/2 doesn't have it +********************************************************************/ +int sys_chown(char *fname,int uid,int gid) +{ +#ifdef NO_CHOWN + DEBUG(1,("Warning - chown(%s,%d,%d) not done\n",fname,uid,gid)); +#else + return(chown(fname,uid,gid)); +#endif +} + +/******************************************************************* +os/2 also doesn't have chroot +********************************************************************/ +int sys_chroot(char *dname) +{ +#ifdef NO_CHROOT + DEBUG(1,("Warning - chroot(%s) not done\n",dname)); +#else + return(chroot(dname)); +#endif +} diff --git a/source3/lib/ufc.c b/source3/lib/ufc.c new file mode 100644 index 0000000000..8417285821 --- /dev/null +++ b/source3/lib/ufc.c @@ -0,0 +1,782 @@ +/* + This bit of code was derived from the UFC-crypt package which + carries the following copyright + + Modified for use by Samba by Andrew Tridgell, October 1994 + + Note that this routine is only faster on some machines. Under Linux 1.1.51 + libc 4.5.26 I actually found this routine to be slightly slower. + + Under SunOS I found a huge speedup by using these routines + (a factor of 20 or so) + + Warning: I've had a report from Steve Kennedy <steve@gbnet.org> + that this crypt routine may sometimes get the wrong answer. Only + use UFC_CRYT if you really need it. + +*/ + +#ifdef UFC_CRYPT + +/* + * UFC-crypt: ultra fast crypt(3) implementation + * + * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * @(#)crypt_util.c 2.31 02/08/92 + * + * Support routines + * + */ +#include "includes.h" + + +#ifndef long32 +#define long32 int32 +#endif + +#ifndef long64 +#define long64 int64 +#endif + +#ifndef ufc_long +#define ufc_long unsigned +#endif + +#ifndef _UFC_64_ +#define _UFC_32_ +#endif + +/* + * Permutation done once on the 56 bit + * key derived from the original 8 byte ASCII key. + */ +static int pc1[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +/* + * How much to rotate each 28 bit half of the pc1 permutated + * 56 bit key before using pc2 to give the i' key + */ +static int rots[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +/* + * Permutation giving the key + * of the i' DES round + */ +static int pc2[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * The E expansion table which selects + * bits from the 32 bit intermediate result. + */ +static int esel[48] = { + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 +}; +static int e_inverse[64]; + +/* + * Permutation done on the + * result of sbox lookups + */ +static int perm32[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +/* + * The sboxes + */ +static int sbox[8][4][16]= { + { { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7 }, + { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8 }, + { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0 }, + { 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 } + }, + + { { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10 }, + { 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5 }, + { 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15 }, + { 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 } + }, + + { { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8 }, + { 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1 }, + { 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7 }, + { 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 } + }, + + { { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15 }, + { 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9 }, + { 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4 }, + { 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 } + }, + + { { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9 }, + { 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6 }, + { 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14 }, + { 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 } + }, + + { { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11 }, + { 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8 }, + { 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6 }, + { 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 } + }, + + { { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1 }, + { 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6 }, + { 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2 }, + { 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 } + }, + + { { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7 }, + { 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2 }, + { 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8 }, + { 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } + } +}; + +/* + * This is the final + * permutation matrix + */ +static int final_perm[64] = { + 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 +}; + +/* + * The 16 DES keys in BITMASK format + */ +#ifdef _UFC_32_ +long32 _ufc_keytab[16][2]; +#endif + +#ifdef _UFC_64_ +long64 _ufc_keytab[16]; +#endif + + +#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.') +#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') + +/* Macro to set a bit (0..23) */ +#define BITMASK(i) ( (1<<(11-(i)%12+3)) << ((i)<12?16:0) ) + +/* + * sb arrays: + * + * Workhorses of the inner loop of the DES implementation. + * They do sbox lookup, shifting of this value, 32 bit + * permutation and E permutation for the next round. + * + * Kept in 'BITMASK' format. + */ + +#ifdef _UFC_32_ +long32 _ufc_sb0[8192], _ufc_sb1[8192], _ufc_sb2[8192], _ufc_sb3[8192]; +static long32 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; +#endif + +#ifdef _UFC_64_ +long64 _ufc_sb0[4096], _ufc_sb1[4096], _ufc_sb2[4096], _ufc_sb3[4096]; +static long64 *sb[4] = {_ufc_sb0, _ufc_sb1, _ufc_sb2, _ufc_sb3}; +#endif + +/* + * eperm32tab: do 32 bit permutation and E selection + * + * The first index is the byte number in the 32 bit value to be permuted + * - second - is the value of this byte + * - third - selects the two 32 bit values + * + * The table is used and generated internally in init_des to speed it up + */ +static ufc_long eperm32tab[4][256][2]; + +/* + * do_pc1: permform pc1 permutation in the key schedule generation. + * + * The first index is the byte number in the 8 byte ASCII key + * - second - - the two 28 bits halfs of the result + * - third - selects the 7 bits actually used of each byte + * + * The result is kept with 28 bit per 32 bit with the 4 most significant + * bits zero. + */ +static ufc_long do_pc1[8][2][128]; + +/* + * do_pc2: permform pc2 permutation in the key schedule generation. + * + * The first index is the septet number in the two 28 bit intermediate values + * - second - - - septet values + * + * Knowledge of the structure of the pc2 permutation is used. + * + * The result is kept with 28 bit per 32 bit with the 4 most significant + * bits zero. + */ +static ufc_long do_pc2[8][128]; + +/* + * efp: undo an extra e selection and do final + * permutation giving the DES result. + * + * Invoked 6 bit a time on two 48 bit values + * giving two 32 bit longs. + */ +static ufc_long efp[16][64][2]; + +static unsigned char bytemask[8] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 +}; + +static ufc_long longmask[32] = { + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + + +/* + * Silly rewrite of 'bzero'. I do so + * because some machines don't have + * bzero and some don't have memset. + */ + +static void clearmem(start, cnt) + char *start; + int cnt; + { while(cnt--) + *start++ = '\0'; + } + +static int initialized = 0; + +/* lookup a 6 bit value in sbox */ + +#define s_lookup(i,s) sbox[(i)][(((s)>>4) & 0x2)|((s) & 0x1)][((s)>>1) & 0xf]; + +/* + * Initialize unit - may be invoked directly + * by fcrypt users. + */ + +static void ufc_init_des() + { int comes_from_bit; + int bit, sg; + ufc_long j; + ufc_long mask1, mask2; + + /* + * Create the do_pc1 table used + * to affect pc1 permutation + * when generating keys + */ + for(bit = 0; bit < 56; bit++) { + comes_from_bit = pc1[bit] - 1; + mask1 = bytemask[comes_from_bit % 8 + 1]; + mask2 = longmask[bit % 28 + 4]; + for(j = 0; j < 128; j++) { + if(j & mask1) + do_pc1[comes_from_bit / 8][bit / 28][j] |= mask2; + } + } + + /* + * Create the do_pc2 table used + * to affect pc2 permutation when + * generating keys + */ + for(bit = 0; bit < 48; bit++) { + comes_from_bit = pc2[bit] - 1; + mask1 = bytemask[comes_from_bit % 7 + 1]; + mask2 = BITMASK(bit % 24); + for(j = 0; j < 128; j++) { + if(j & mask1) + do_pc2[comes_from_bit / 7][j] |= mask2; + } + } + + /* + * Now generate the table used to do combined + * 32 bit permutation and e expansion + * + * We use it because we have to permute 16384 32 bit + * longs into 48 bit in order to initialize sb. + * + * Looping 48 rounds per permutation becomes + * just too slow... + * + */ + + clearmem((char*)eperm32tab, sizeof(eperm32tab)); + + for(bit = 0; bit < 48; bit++) { + ufc_long mask1,comes_from; + + comes_from = perm32[esel[bit]-1]-1; + mask1 = bytemask[comes_from % 8]; + + for(j = 256; j--;) { + if(j & mask1) + eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24); + } + } + + /* + * Create the sb tables: + * + * For each 12 bit segment of an 48 bit intermediate + * result, the sb table precomputes the two 4 bit + * values of the sbox lookups done with the two 6 + * bit halves, shifts them to their proper place, + * sends them through perm32 and finally E expands + * them so that they are ready for the next + * DES round. + * + */ + for(sg = 0; sg < 4; sg++) { + int j1, j2; + int s1, s2; + + for(j1 = 0; j1 < 64; j1++) { + s1 = s_lookup(2 * sg, j1); + for(j2 = 0; j2 < 64; j2++) { + ufc_long to_permute, inx; + + s2 = s_lookup(2 * sg + 1, j2); + to_permute = ((s1 << 4) | s2) << (24 - 8 * sg); + +#ifdef _UFC_32_ + inx = ((j1 << 6) | j2) << 1; + sb[sg][inx ] = eperm32tab[0][(to_permute >> 24) & 0xff][0]; + sb[sg][inx+1] = eperm32tab[0][(to_permute >> 24) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[1][(to_permute >> 16) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[1][(to_permute >> 16) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[2][(to_permute >> 8) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[2][(to_permute >> 8) & 0xff][1]; + sb[sg][inx ] |= eperm32tab[3][(to_permute) & 0xff][0]; + sb[sg][inx+1] |= eperm32tab[3][(to_permute) & 0xff][1]; +#endif +#ifdef _UFC_64_ + inx = ((j1 << 6) | j2); + sb[sg][inx] = + ((long64)eperm32tab[0][(to_permute >> 24) & 0xff][0] << 32) | + (long64)eperm32tab[0][(to_permute >> 24) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[1][(to_permute >> 16) & 0xff][0] << 32) | + (long64)eperm32tab[1][(to_permute >> 16) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[2][(to_permute >> 8) & 0xff][0] << 32) | + (long64)eperm32tab[2][(to_permute >> 8) & 0xff][1]; + sb[sg][inx] |= + ((long64)eperm32tab[3][(to_permute) & 0xff][0] << 32) | + (long64)eperm32tab[3][(to_permute) & 0xff][1]; +#endif + } + } + } + + /* + * Create an inverse matrix for esel telling + * where to plug out bits if undoing it + */ + for(bit=48; bit--;) { + e_inverse[esel[bit] - 1 ] = bit; + e_inverse[esel[bit] - 1 + 32] = bit + 48; + } + + /* + * create efp: the matrix used to + * undo the E expansion and effect final permutation + */ + clearmem((char*)efp, sizeof efp); + for(bit = 0; bit < 64; bit++) { + int o_bit, o_long; + ufc_long word_value, mask1, mask2; + int comes_from_f_bit, comes_from_e_bit; + int comes_from_word, bit_within_word; + + /* See where bit i belongs in the two 32 bit long's */ + o_long = bit / 32; /* 0..1 */ + o_bit = bit % 32; /* 0..31 */ + + /* + * And find a bit in the e permutated value setting this bit. + * + * Note: the e selection may have selected the same bit several + * times. By the initialization of e_inverse, we only look + * for one specific instance. + */ + comes_from_f_bit = final_perm[bit] - 1; /* 0..63 */ + comes_from_e_bit = e_inverse[comes_from_f_bit]; /* 0..95 */ + comes_from_word = comes_from_e_bit / 6; /* 0..15 */ + bit_within_word = comes_from_e_bit % 6; /* 0..5 */ + + mask1 = longmask[bit_within_word + 26]; + mask2 = longmask[o_bit]; + + for(word_value = 64; word_value--;) { + if(word_value & mask1) + efp[comes_from_word][word_value][o_long] |= mask2; + } + } + initialized++; + } + +/* + * Process the elements of the sb table permuting the + * bits swapped in the expansion by the current salt. + */ + +#ifdef _UFC_32_ +static void shuffle_sb(k, saltbits) + long32 *k; + ufc_long saltbits; + { ufc_long j; + long32 x; + for(j=4096; j--;) { + x = (k[0] ^ k[1]) & (long32)saltbits; + *k++ ^= x; + *k++ ^= x; + } + } +#endif + +#ifdef _UFC_64_ +static void shuffle_sb(k, saltbits) + long64 *k; + ufc_long saltbits; + { ufc_long j; + long64 x; + for(j=4096; j--;) { + x = ((*k >> 32) ^ *k) & (long64)saltbits; + *k++ ^= (x << 32) | x; + } + } +#endif + +/* + * Setup the unit for a new salt + * Hopefully we'll not see a new salt in each crypt call. + */ + +static unsigned char current_salt[3] = "&&"; /* invalid value */ +static ufc_long current_saltbits = 0; +static int direction = 0; + +static void setup_salt(char *s1) + { ufc_long i, j, saltbits; + unsigned char *s2 = (unsigned char *)s1; + + if(!initialized) + ufc_init_des(); + + if(s2[0] == current_salt[0] && s2[1] == current_salt[1]) + return; + current_salt[0] = s2[0]; current_salt[1] = s2[1]; + + /* + * This is the only crypt change to DES: + * entries are swapped in the expansion table + * according to the bits set in the salt. + */ + saltbits = 0; + for(i = 0; i < 2; i++) { + long c=ascii_to_bin(s2[i]); + if(c < 0 || c > 63) + c = 0; + for(j = 0; j < 6; j++) { + if((c >> j) & 0x1) + saltbits |= BITMASK(6 * i + j); + } + } + + /* + * Permute the sb table values + * to reflect the changed e + * selection table + */ + shuffle_sb(_ufc_sb0, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb1, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb2, current_saltbits ^ saltbits); + shuffle_sb(_ufc_sb3, current_saltbits ^ saltbits); + + current_saltbits = saltbits; + } + +static void ufc_mk_keytab(key) + char *key; + { ufc_long v1, v2, *k1; + int i; +#ifdef _UFC_32_ + long32 v, *k2 = &_ufc_keytab[0][0]; +#endif +#ifdef _UFC_64_ + long64 v, *k2 = &_ufc_keytab[0]; +#endif + + v1 = v2 = 0; k1 = &do_pc1[0][0][0]; + for(i = 8; i--;) { + v1 |= k1[*key & 0x7f]; k1 += 128; + v2 |= k1[*key++ & 0x7f]; k1 += 128; + } + + for(i = 0; i < 16; i++) { + k1 = &do_pc2[0][0]; + + v1 = (v1 << rots[i]) | (v1 >> (28 - rots[i])); + v = k1[(v1 >> 21) & 0x7f]; k1 += 128; + v |= k1[(v1 >> 14) & 0x7f]; k1 += 128; + v |= k1[(v1 >> 7) & 0x7f]; k1 += 128; + v |= k1[(v1 ) & 0x7f]; k1 += 128; + +#ifdef _UFC_32_ + *k2++ = v; + v = 0; +#endif +#ifdef _UFC_64_ + v <<= 32; +#endif + + v2 = (v2 << rots[i]) | (v2 >> (28 - rots[i])); + v |= k1[(v2 >> 21) & 0x7f]; k1 += 128; + v |= k1[(v2 >> 14) & 0x7f]; k1 += 128; + v |= k1[(v2 >> 7) & 0x7f]; k1 += 128; + v |= k1[(v2 ) & 0x7f]; + + *k2++ = v; + } + + direction = 0; + } + +/* + * Undo an extra E selection and do final permutations + */ + +ufc_long *_ufc_dofinalperm(l1, l2, r1, r2) + ufc_long l1,l2,r1,r2; + { ufc_long v1, v2, x; + static ufc_long ary[2]; + + x = (l1 ^ l2) & current_saltbits; l1 ^= x; l2 ^= x; + x = (r1 ^ r2) & current_saltbits; r1 ^= x; r2 ^= x; + + v1=v2=0; l1 >>= 3; l2 >>= 3; r1 >>= 3; r2 >>= 3; + + v1 |= efp[15][ r2 & 0x3f][0]; v2 |= efp[15][ r2 & 0x3f][1]; + v1 |= efp[14][(r2 >>= 6) & 0x3f][0]; v2 |= efp[14][ r2 & 0x3f][1]; + v1 |= efp[13][(r2 >>= 10) & 0x3f][0]; v2 |= efp[13][ r2 & 0x3f][1]; + v1 |= efp[12][(r2 >>= 6) & 0x3f][0]; v2 |= efp[12][ r2 & 0x3f][1]; + + v1 |= efp[11][ r1 & 0x3f][0]; v2 |= efp[11][ r1 & 0x3f][1]; + v1 |= efp[10][(r1 >>= 6) & 0x3f][0]; v2 |= efp[10][ r1 & 0x3f][1]; + v1 |= efp[ 9][(r1 >>= 10) & 0x3f][0]; v2 |= efp[ 9][ r1 & 0x3f][1]; + v1 |= efp[ 8][(r1 >>= 6) & 0x3f][0]; v2 |= efp[ 8][ r1 & 0x3f][1]; + + v1 |= efp[ 7][ l2 & 0x3f][0]; v2 |= efp[ 7][ l2 & 0x3f][1]; + v1 |= efp[ 6][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 6][ l2 & 0x3f][1]; + v1 |= efp[ 5][(l2 >>= 10) & 0x3f][0]; v2 |= efp[ 5][ l2 & 0x3f][1]; + v1 |= efp[ 4][(l2 >>= 6) & 0x3f][0]; v2 |= efp[ 4][ l2 & 0x3f][1]; + + v1 |= efp[ 3][ l1 & 0x3f][0]; v2 |= efp[ 3][ l1 & 0x3f][1]; + v1 |= efp[ 2][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 2][ l1 & 0x3f][1]; + v1 |= efp[ 1][(l1 >>= 10) & 0x3f][0]; v2 |= efp[ 1][ l1 & 0x3f][1]; + v1 |= efp[ 0][(l1 >>= 6) & 0x3f][0]; v2 |= efp[ 0][ l1 & 0x3f][1]; + + ary[0] = v1; ary[1] = v2; + return ary; + } + +/* + * crypt only: convert from 64 bit to 11 bit ASCII + * prefixing with the salt + */ + +static char *output_conversion(v1, v2, salt) + ufc_long v1, v2; + char *salt; + { static char outbuf[14]; + int i, s; + + outbuf[0] = salt[0]; + outbuf[1] = salt[1] ? salt[1] : salt[0]; + + for(i = 0; i < 5; i++) + outbuf[i + 2] = bin_to_ascii((v1 >> (26 - 6 * i)) & 0x3f); + + s = (v2 & 0xf) << 2; + v2 = (v2 >> 2) | ((v1 & 0x3) << 30); + + for(i = 5; i < 10; i++) + outbuf[i + 2] = bin_to_ascii((v2 >> (56 - 6 * i)) & 0x3f); + + outbuf[12] = bin_to_ascii(s); + outbuf[13] = 0; + + return outbuf; + } + +ufc_long *_ufc_doit(); + +/* + * UNIX crypt function + */ + +char *ufc_crypt(char *key,char *salt) + { ufc_long *s; + char ktab[9]; + + /* + * Hack DES tables according to salt + */ + setup_salt(salt); + + /* + * Setup key schedule + */ + clearmem(ktab, sizeof ktab); + StrnCpy(ktab, key, 8); + ufc_mk_keytab(ktab); + + /* + * Go for the 25 DES encryptions + */ + s = _ufc_doit((ufc_long)0, (ufc_long)0, + (ufc_long)0, (ufc_long)0, (ufc_long)25); + + /* + * And convert back to 6 bit ASCII + */ + return output_conversion(s[0], s[1], salt); + } + + +#ifdef _UFC_32_ + +/* + * 32 bit version + */ + +extern long32 _ufc_keytab[16][2]; +extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; + +#define SBA(sb, v) (*(long32*)((char*)(sb)+(v))) + +ufc_long *_ufc_doit(l1, l2, r1, r2, itr) + ufc_long l1, l2, r1, r2, itr; + { int i; + long32 s, *k; + + while(itr--) { + k = &_ufc_keytab[0][0]; + for(i=8; i--; ) { + s = *k++ ^ r1; + l1 ^= SBA(_ufc_sb1, s & 0xffff); l2 ^= SBA(_ufc_sb1, (s & 0xffff)+4); + l1 ^= SBA(_ufc_sb0, s >>= 16); l2 ^= SBA(_ufc_sb0, (s) +4); + s = *k++ ^ r2; + l1 ^= SBA(_ufc_sb3, s & 0xffff); l2 ^= SBA(_ufc_sb3, (s & 0xffff)+4); + l1 ^= SBA(_ufc_sb2, s >>= 16); l2 ^= SBA(_ufc_sb2, (s) +4); + + s = *k++ ^ l1; + r1 ^= SBA(_ufc_sb1, s & 0xffff); r2 ^= SBA(_ufc_sb1, (s & 0xffff)+4); + r1 ^= SBA(_ufc_sb0, s >>= 16); r2 ^= SBA(_ufc_sb0, (s) +4); + s = *k++ ^ l2; + r1 ^= SBA(_ufc_sb3, s & 0xffff); r2 ^= SBA(_ufc_sb3, (s & 0xffff)+4); + r1 ^= SBA(_ufc_sb2, s >>= 16); r2 ^= SBA(_ufc_sb2, (s) +4); + } + s=l1; l1=r1; r1=s; s=l2; l2=r2; r2=s; + } + return _ufc_dofinalperm(l1, l2, r1, r2); + } + +#endif + +#ifdef _UFC_64_ + +/* + * 64 bit version + */ + +extern long64 _ufc_keytab[16]; +extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; + +#define SBA(sb, v) (*(long64*)((char*)(sb)+(v))) + +ufc_long *_ufc_doit(l1, l2, r1, r2, itr) + ufc_long l1, l2, r1, r2, itr; + { int i; + long64 l, r, s, *k; + + l = (((long64)l1) << 32) | ((long64)l2); + r = (((long64)r1) << 32) | ((long64)r2); + + while(itr--) { + k = &_ufc_keytab[0]; + for(i=8; i--; ) { + s = *k++ ^ r; + l ^= SBA(_ufc_sb3, (s >> 0) & 0xffff); + l ^= SBA(_ufc_sb2, (s >> 16) & 0xffff); + l ^= SBA(_ufc_sb1, (s >> 32) & 0xffff); + l ^= SBA(_ufc_sb0, (s >> 48) & 0xffff); + + s = *k++ ^ l; + r ^= SBA(_ufc_sb3, (s >> 0) & 0xffff); + r ^= SBA(_ufc_sb2, (s >> 16) & 0xffff); + r ^= SBA(_ufc_sb1, (s >> 32) & 0xffff); + r ^= SBA(_ufc_sb0, (s >> 48) & 0xffff); + } + s=l; l=r; r=s; + } + + l1 = l >> 32; l2 = l & 0xffffffff; + r1 = r >> 32; r2 = r & 0xffffffff; + return _ufc_dofinalperm(l1, l2, r1, r2); + } + +#endif + + +#else +int ufc_dummy_procedure(void) +{return 0;} +#endif diff --git a/source3/lib/username.c b/source3/lib/username.c new file mode 100644 index 0000000000..3d214fbbda --- /dev/null +++ b/source3/lib/username.c @@ -0,0 +1,246 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Username handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +extern int DEBUGLEVEL; + + +/**************************************************************************** +get a users home directory. tries as-is then lower case +****************************************************************************/ +char *get_home_dir(char *user) +{ + static struct passwd *pass; + + pass = Get_Pwnam(user,False); + + if (!pass) return(NULL); + return(pass->pw_dir); +} + + +/******************************************************************* +map a username from a dos name to a unix name by looking in the username +map +********************************************************************/ +void map_username(char *user) +{ + static int depth=0; + static BOOL initialised=False; + static fstring last_from,last_to; + FILE *f; + char *s; + char *mapfile = lp_username_map(); + if (!*mapfile || depth) return; + + if (!*user) return; + + if (!initialised) { + *last_from = *last_to = 0; + initialised = True; + } + + if (strequal(user,last_to)) return; + + if (strequal(user,last_from)) { + DEBUG(3,("Mapped user %s to %s\n",user,last_to)); + strcpy(user,last_to); + return; + } + + f = fopen(mapfile,"r"); + if (!f) { + DEBUG(0,("can't open username map %s\n",mapfile)); + return; + } + + DEBUG(4,("Scanning username map %s\n",mapfile)); + + depth++; + + for (; (s=fgets_slash(NULL,80,f)); free(s)) { + char *unixname = s; + char *dosname = strchr(unixname,'='); + + if (!dosname) continue; + *dosname++ = 0; + + while (isspace(*unixname)) unixname++; + if (!*unixname || strchr("#;",*unixname)) continue; + + { + int l = strlen(unixname); + while (l && isspace(unixname[l-1])) { + unixname[l-1] = 0; + l--; + } + } + + if (strchr(dosname,'*') || user_in_list(user,dosname)) { + DEBUG(3,("Mapped user %s to %s\n",user,unixname)); + StrnCpy(last_from,user,sizeof(last_from)-1); + sscanf(unixname,"%s",user); + StrnCpy(last_to,user,sizeof(last_to)-1); + } + } + + fclose(f); + + depth--; +} + +/**************************************************************************** +internals of Get_Pwnam wrapper +****************************************************************************/ +static struct passwd *_Get_Pwnam(char *s) +{ + struct passwd *ret; + + ret = getpwnam(s); + if (ret) + { +#ifdef GETPWANAM + struct passwd_adjunct *pwret; + pwret = getpwanam(s); + if (pwret) + { + free(ret->pw_passwd); + ret->pw_passwd = pwret->pwa_passwd; + } +#endif + + } + + return(ret); +} + + +/**************************************************************************** +a wrapper for getpwnam() that tries with all lower and all upper case +if the initial name fails. Also tried with first letter capitalised +Note that this changes user! +****************************************************************************/ +struct passwd *Get_Pwnam(char *user,BOOL allow_change) +{ + fstring user2; + + struct passwd *ret; + + if (!user || !(*user)) + return(NULL); + + StrnCpy(user2,user,sizeof(user2)-1); + + if (!allow_change) { + user = &user2[0]; + } + + map_username(user); + + ret = _Get_Pwnam(user); + if (ret) return(ret); + + strlower(user); + ret = _Get_Pwnam(user); + if (ret) return(ret); + + strupper(user); + ret = _Get_Pwnam(user); + if (ret) return(ret); + + /* try with first letter capitalised */ + if (strlen(user) > 1) + strlower(user+1); + ret = _Get_Pwnam(user); + if (ret) return(ret); + + if (allow_change) + strcpy(user,user2); + + return(NULL); +} + + +/**************************************************************************** +check if a user is in a user list +****************************************************************************/ +BOOL user_in_list(char *user,char *list) +{ + pstring tok; + char *p=list; + + while (next_token(&p,tok,LIST_SEP)) + { + if (strequal(user,tok)) + return(True); + +#ifdef NETGROUP + if (*tok == '@') + { + static char *mydomain = NULL; + if (mydomain == 0) + yp_get_default_domain(&mydomain); + + DEBUG(5,("looking for user %s of domain %s in netgroup %s\n", + user, mydomain, &tok[1])); + DEBUG(5,("innetgr is %s\n", + innetgr(&tok[1], (char *) 0, user, mydomain) + ? "TRUE" : "FALSE")); + + if (innetgr(&tok[1], (char *)0, user, mydomain)) + return (True); + } +#endif + + +#if HAVE_GETGRNAM + if (*tok == '@') + { + struct group *gptr; + char **member; + struct passwd *pass = Get_Pwnam(user,False); + + if (pass) { + gptr = getgrgid(pass->pw_gid); + if (gptr && strequal(gptr->gr_name,&tok[1])) + return(True); + } + + gptr = (struct group *)getgrnam(&tok[1]); + + if (gptr) + { + member = gptr->gr_mem; + while (member && *member) + { + if (strequal(*member,user)) + return(True); + member++; + } + } + } +#endif + } + return(False); +} + + diff --git a/source3/lib/util.c b/source3/lib/util.c new file mode 100644 index 0000000000..7bd6298c4c --- /dev/null +++ b/source3/lib/util.c @@ -0,0 +1,4510 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" + +pstring scope = ""; + +int DEBUGLEVEL = 1; + +BOOL passive = False; + +int Protocol = PROTOCOL_COREPLUS; + +int serverzone=0; + +/* a default finfo structure to ensure all fields are sensible */ +file_info def_finfo = {-1,0,0,0,0,0,0,""}; + +/* these are some file handles where debug info will be stored */ +FILE *dbf = NULL; + +/* the client file descriptor */ +int Client = -1; + +/* info on the client */ +struct from_host Client_info= +{"UNKNOWN","0.0.0.0",NULL}; + +/* the last IP received from */ +struct in_addr lastip; + +/* the last port received from */ +int lastport=0; + +/* my IP, the broadcast IP and the Netmask */ +struct in_addr myip; +struct in_addr bcast_ip; +struct in_addr Netmask; + +int trans_num = 0; + +/* + case handling on filenames +*/ +int case_default = CASE_LOWER; + + +/* size of reads during a direct file to file transfer */ +int ReadSize = 16*1024; + +pstring debugf = "/tmp/log.samba"; +int syslog_level; + +/* the following control case operations - they are put here so the + client can link easily */ +BOOL case_sensitive; +BOOL case_preserve; +BOOL use_mangled_map = False; +BOOL short_case_preserve; +BOOL case_mangle; + +fstring remote_machine=""; +fstring local_machine=""; +fstring remote_arch="UNKNOWN"; +fstring remote_proto="UNKNOWN"; +pstring myhostname=""; +pstring user_socket_options=""; +pstring sesssetup_user=""; + + +static char *filename_dos(char *path,char *buf); + +static BOOL stdout_logging = False; + + +/******************************************************************* + get ready for syslog stuff + ******************************************************************/ +void setup_logging(char *pname,BOOL interactive) +{ +#ifdef SYSLOG + if (!interactive) { + char *p = strrchr(pname,'/'); + if (p) pname = p+1; + openlog(pname, LOG_PID, LOG_DAEMON); + } +#endif + if (interactive) { + stdout_logging = True; + dbf = stdout; + } +} + + +BOOL append_log=False; + + +/**************************************************************************** +reopen the log files +****************************************************************************/ +void reopen_logs(void) +{ + extern FILE *dbf; + pstring fname; + + if (DEBUGLEVEL > 0) + { + strcpy(fname,debugf); + if (lp_loaded() && (*lp_logfile())) + strcpy(fname,lp_logfile()); + + if (!strcsequal(fname,debugf) || !dbf || !file_exist(debugf,NULL)) + { + strcpy(debugf,fname); + if (dbf) fclose(dbf); + if (append_log) + dbf = fopen(debugf,"a"); + else + dbf = fopen(debugf,"w"); + if (dbf) setbuf(dbf,NULL); + } + } + else + { + if (dbf) + { + fclose(dbf); + dbf = NULL; + } + } +} + + +/******************************************************************* +write an debug message on the debugfile. This is called by the DEBUG +macro +********************************************************************/ +#ifdef __STDC__ +int Debug1(char *format_str, ...) +{ +#else +int Debug1(va_alist) +va_dcl +{ + char *format_str; +#endif + va_list ap; + +#ifdef __STDC__ + va_start(ap, format_str); +#else + va_start(ap); + format_str = va_arg(ap,char *); +#endif + + if (stdout_logging) { + vfprintf(dbf,format_str,ap); + va_end(ap); + return(0); + } + + { + static int debug_count=0; + + debug_count++; + if (debug_count == 100) { + int maxlog = lp_max_log_size() * 1024; + if (dbf && maxlog > 0) + { + struct stat st; + + if (fstat(fileno(dbf),&st) == 0 && st.st_size > maxlog) { + fclose(dbf); dbf = NULL; + reopen_logs(); + if (dbf && file_size(debugf) > maxlog) { + pstring name; + fclose(dbf); dbf = NULL; + sprintf(name,"%s.old",debugf); + sys_rename(debugf,name); + reopen_logs(); + } + } + } + debug_count=0; + } + } + +#ifdef SYSLOG + if (!lp_syslog_only()) +#endif + { + if (!dbf) + { + dbf = fopen(debugf,"w"); + if (dbf) + setbuf(dbf,NULL); + else + return(0); + } + } + +#ifdef SYSLOG + if (syslog_level < lp_syslog()) + { + /* + * map debug levels to syslog() priorities + * note that not all DEBUG(0, ...) calls are + * necessarily errors + */ + static int priority_map[] = { + LOG_ERR, /* 0 */ + LOG_WARNING, /* 1 */ + LOG_NOTICE, /* 2 */ + LOG_INFO, /* 3 */ + }; + int priority; + pstring msgbuf; + + if (syslog_level >= sizeof(priority_map) / sizeof(priority_map[0]) || + syslog_level < 0) + priority = LOG_DEBUG; + else + priority = priority_map[syslog_level]; + + vsprintf(msgbuf, format_str, ap); + + msgbuf[255] = '\0'; + syslog(priority, "%s", msgbuf); + } +#endif + +#ifdef SYSLOG + if (!lp_syslog_only()) +#endif + { + vfprintf(dbf,format_str,ap); + fflush(dbf); + } + + va_end(ap); + return(0); +} + +/**************************************************************************** +routine to do file locking +****************************************************************************/ +BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type) +{ +#if HAVE_FCNTL_LOCK + struct flock lock; + int ret; + +#if 1 + uint32 mask = 0xC0000000; + + /* make sure the count is reasonable, we might kill the lockd otherwise */ + count &= ~mask; + + /* the offset is often strange - remove 2 of its bits if either of + the top two bits are set. Shift the top ones by two bits. This + still allows OLE2 apps to operate, but should stop lockd from + dieing */ + if ((offset & mask) != 0) + offset = (offset & ~mask) | ((offset & mask) >> 2); +#else + unsigned long mask = ((unsigned)1<<31); + + /* interpret negative counts as large numbers */ + if (count < 0) + count &= ~mask; + + /* no negative offsets */ + offset &= ~mask; + + /* count + offset must be in range */ + while ((offset < 0 || (offset + count < 0)) && mask) + { + offset &= ~mask; + mask = mask >> 1; + } +#endif + + + DEBUG(5,("fcntl_lock %d %d %d %d %d\n",fd,op,(int)offset,(int)count,type)); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = (int)offset; + lock.l_len = (int)count; + lock.l_pid = 0; + + errno = 0; + + ret = fcntl(fd,op,&lock); + + if (errno != 0) + DEBUG(3,("fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); + + /* a lock query */ + if (op == F_GETLK) + { + if ((ret != -1) && + (lock.l_type != F_UNLCK) && + (lock.l_pid != 0) && + (lock.l_pid != getpid())) + { + DEBUG(3,("fd %d is locked by pid %d\n",fd,lock.l_pid)); + return(True); + } + + /* it must be not locked or locked by me */ + return(False); + } + + /* a lock set or unset */ + if (ret == -1) + { + DEBUG(3,("lock failed at offset %d count %d op %d type %d (%s)\n", + offset,count,op,type,strerror(errno))); + + /* perhaps it doesn't support this sort of locking?? */ + if (errno == EINVAL) + { + DEBUG(3,("locking not supported? returning True\n")); + return(True); + } + + return(False); + } + + /* everything went OK */ + DEBUG(5,("Lock call successful\n")); + + return(True); +#else + return(False); +#endif +} + +/******************************************************************* +lock a file - returning a open file descriptor or -1 on failure +The timeout is in seconds. 0 means no timeout +********************************************************************/ +int file_lock(char *name,int timeout) +{ + int fd = open(name,O_RDWR|O_CREAT,0666); + time_t t=0; + if (fd < 0) return(-1); + +#if HAVE_FCNTL_LOCK + if (timeout) t = time(NULL); + while (!timeout || (time(NULL)-t < timeout)) { + if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)) return(fd); + msleep(LOCK_RETRY_TIMEOUT); + } + return(-1); +#else + return(fd); +#endif +} + +/******************************************************************* +unlock a file locked by file_lock +********************************************************************/ +void file_unlock(int fd) +{ + if (fd<0) return; +#if HAVE_FCNTL_LOCK + fcntl_lock(fd,F_SETLK,0,1,F_UNLCK); +#endif + close(fd); +} + +/******************************************************************* +a gettimeofday wrapper +********************************************************************/ +void GetTimeOfDay(struct timeval *tval) +{ +#ifdef GETTIMEOFDAY1 + gettimeofday(tval); +#else + gettimeofday(tval,NULL); +#endif +} + +int extra_time_offset = 0; + +static int timediff = 0; + +/******************************************************************* +init the time differences +********************************************************************/ +void TimeInit(void) +{ + struct tm tm_utc,tm_local; + time_t t; + + t = time(NULL); + + tm_utc = *(gmtime(&t)); + tm_local = *(localtime(&t)); + +#ifdef HAVE_GMTOFF + timediff = -tm_local.tm_gmtoff; +#else + timediff = mktime(&tm_utc) - mktime(&tm_local); +#endif + + if (serverzone == 0) { + serverzone = timediff - DSTDiff(t); + DEBUG(4,("Serverzone is %d\n",serverzone)); + } +} + + +/******************************************************************* +return the DST offset for a particular time +We keep a table of DST offsets to prevent calling localtime() on each +call of this function. This saves a LOT of time on many unixes. +********************************************************************/ +int DSTDiff(time_t t) +{ + static struct dst_table {time_t start,end; BOOL is_dst;} *dst_table = NULL; + static int table_size = 0; + int i; + BOOL is_dst = False; + + if (t == 0) t = time(NULL); + +#ifndef NO_ISDST + for (i=0;i<table_size;i++) + if (t >= dst_table[i].start && t <= dst_table[i].end) break; + + if (i<table_size) { + is_dst = dst_table[i].is_dst; + } else { + time_t low,high; + + dst_table = (struct dst_table *)Realloc(dst_table, + sizeof(dst_table[0])*(i+1)); + if (!dst_table) { + table_size = 0; + return(0); + } + + table_size++; + + dst_table[i].is_dst = is_dst = (localtime(&t)->tm_isdst?True:False);; + dst_table[i].start = dst_table[i].end = t; + + /* no entry will cover more than 6 months */ + low = t - 3*30*24*60*60; + high = t + 3*30*24*60*60; + + /* widen the new entry using two bisection searches */ + while (low+60*60 < dst_table[i].start) { + t = low + (dst_table[i].start-low)/2; + if ((localtime(&t)->tm_isdst?True:False) == is_dst) + dst_table[i].start = t; + else + low = t; + } + + while (high-60*60 > dst_table[i].end) { + t = high + (high-dst_table[i].end)/2; + if ((localtime(&t)->tm_isdst?True:False) == is_dst) + dst_table[i].end = t; + else + high = t; + } + +/* + DEBUG(1,("Added DST entry from %s ", + asctime(localtime(&dst_table[i].start)))); + DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)), + dst_table[i].is_dst)); +*/ + } +#endif + + return((is_dst?60*60:0) - (extra_time_offset*60)); +} + +/**************************************************************************** +return the difference between local and GMT time +****************************************************************************/ +int TimeDiff(time_t t) +{ + static BOOL initialised = False; + if (!initialised) {initialised=True; TimeInit();} + return(timediff - DSTDiff(t)); +} + +/**************************************************************************** +try to optimise the localtime call, it can be quite expenive on some machines +timemul is normally LOCAL_TO_GMT, GMT_TO_LOCAL or 0 +****************************************************************************/ +struct tm *LocalTime(time_t *t,int timemul) +{ + time_t t2 = *t; + + if (timemul) + t2 += timemul * TimeDiff(t2); + + return(gmtime(&t2)); +} + + +/**************************************************************************** +determine if a file descriptor is in fact a socket +****************************************************************************/ +BOOL is_a_socket(int fd) +{ + int v,l; + l = sizeof(int); + return(getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0); +} + + +static char *last_ptr=NULL; + +/**************************************************************************** + Get the next token from a string, return False if none found + handles double-quotes. +Based on a routine by GJC@VILLAGE.COM. +Extensively modified by Andrew.Tridgell@anu.edu.au +****************************************************************************/ +BOOL next_token(char **ptr,char *buff,char *sep) +{ + char *s; + BOOL quoted; + + if (!ptr) ptr = &last_ptr; + if (!ptr) return(False); + + s = *ptr; + + /* default to simple separators */ + if (!sep) sep = " \t\n\r"; + + /* find the first non sep char */ + while(*s && strchr(sep,*s)) s++; + + /* nothing left? */ + if (! *s) return(False); + + /* copy over the token */ + for (quoted = False; *s && (quoted || !strchr(sep,*s)); s++) + { + if (*s == '\"') + quoted = !quoted; + else + *buff++ = *s; + } + + *ptr = (*s) ? s+1 : s; + *buff = 0; + last_ptr = *ptr; + + return(True); +} + +/**************************************************************************** +Convert list of tokens to array; dependent on above routine. +Uses last_ptr from above - bit of a hack. +****************************************************************************/ +char **toktocliplist(int *ctok, char *sep) +{ + char *s=last_ptr; + int ictok=0; + char **ret, **iret; + + if (!sep) sep = " \t\n\r"; + + while(*s && strchr(sep,*s)) s++; + + /* nothing left? */ + if (!*s) return(NULL); + + do { + ictok++; + while(*s && (!strchr(sep,*s))) s++; + while(*s && strchr(sep,*s)) *s++=0; + } while(*s); + + *ctok=ictok; + s=last_ptr; + + if (!(ret=iret=malloc(ictok*sizeof(char *)))) return NULL; + + while(ictok--) { + *iret++=s; + while(*s++); + while(!*s) s++; + } + + return ret; +} + +#ifndef HAVE_MEMMOVE +/******************************************************************* +safely copies memory, ensuring no overlap problems. +this is only used if the machine does not have it's own memmove(). +this is not the fastest algorithm in town, but it will do for our +needs. +********************************************************************/ +void *MemMove(void *dest,void *src,int size) +{ + unsigned long d,s; + int i; + if (dest==src || !size) return(dest); + + d = (unsigned long)dest; + s = (unsigned long)src; + + if ((d >= (s+size)) || (s >= (d+size))) { + /* no overlap */ + memcpy(dest,src,size); + return(dest); + } + + if (d < s) + { + /* we can forward copy */ + if (s-d >= sizeof(int) && + !(s%sizeof(int)) && !(d%sizeof(int)) && !(size%sizeof(int))) { + /* do it all as words */ + int *idest = (int *)dest; + int *isrc = (int *)src; + size /= sizeof(int); + for (i=0;i<size;i++) idest[i] = isrc[i]; + } else { + /* simplest */ + char *cdest = (char *)dest; + char *csrc = (char *)src; + for (i=0;i<size;i++) cdest[i] = csrc[i]; + } + } + else + { + /* must backward copy */ + if (d-s >= sizeof(int) && + !(s%sizeof(int)) && !(d%sizeof(int)) && !(size%sizeof(int))) { + /* do it all as words */ + int *idest = (int *)dest; + int *isrc = (int *)src; + size /= sizeof(int); + for (i=size-1;i>=0;i--) idest[i] = isrc[i]; + } else { + /* simplest */ + char *cdest = (char *)dest; + char *csrc = (char *)src; + for (i=size-1;i>=0;i--) cdest[i] = csrc[i]; + } + } + return(dest); +} +#endif + + +/**************************************************************************** +prompte a dptr (to make it recently used) +****************************************************************************/ +void array_promote(char *array,int elsize,int element) +{ + char *p; + if (element == 0) + return; + + p = (char *)malloc(elsize); + + if (!p) + { + DEBUG(5,("Ahh! Can't malloc\n")); + return; + } + memcpy(p,array + element * elsize, elsize); + memmove(array + elsize,array,elsize*element); + memcpy(array,p,elsize); + free(p); +} + +enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON}; + +struct +{ + char *name; + int level; + int option; + int value; + int opttype; +} socket_options[] = { + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL}, + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL}, + {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL}, +#ifdef TCP_NODELAY + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL}, +#endif +#ifdef IPTOS_LOWDELAY + {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON}, +#endif +#ifdef IPTOS_THROUGHPUT + {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON}, +#endif +#ifdef SO_SNDBUF + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT}, +#endif +#ifdef SO_RCVBUF + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT}, +#endif +#ifdef SO_SNDLOWAT + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT}, +#endif +#ifdef SO_RCVLOWAT + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT}, +#endif + {NULL,0,0,0,0}}; + + + +/**************************************************************************** +set user socket options +****************************************************************************/ +void set_socket_options(int fd, char *options) +{ + string tok; + + while (next_token(&options,tok," \t,")) + { + int ret=0,i; + int value = 1; + char *p; + BOOL got_value = False; + + if ((p = strchr(tok,'='))) + { + *p = 0; + value = atoi(p+1); + got_value = True; + } + + for (i=0;socket_options[i].name;i++) + if (strequal(socket_options[i].name,tok)) + break; + + if (!socket_options[i].name) + { + DEBUG(0,("Unknown socket option %s\n",tok)); + continue; + } + + switch (socket_options[i].opttype) + { + case OPT_BOOL: + case OPT_INT: + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option,(char *)&value,sizeof(int)); + break; + + case OPT_ON: + if (got_value) + DEBUG(0,("syntax error - %s does not take a value\n",tok)); + + { + int on = socket_options[i].value; + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option,(char *)&on,sizeof(int)); + } + break; + } + + if (ret != 0) + DEBUG(0,("Failed to set socket option %s\n",tok)); + } +} + + + +/**************************************************************************** + close the socket communication +****************************************************************************/ +void close_sockets(void ) +{ + close(Client); + Client = 0; +} + +/**************************************************************************** + return the date and time as a string +****************************************************************************/ +char *timestring(void ) +{ + static char TimeBuf[100]; + time_t t; + t = time(NULL); +#ifdef NO_STRFTIME + strcpy(TimeBuf, asctime(LocalTime(&t,GMT_TO_LOCAL))); +#elif defined(CLIX) || defined(CONVEX) + strftime(TimeBuf,100,"%m/%d/%y %I:%M:%S %p",LocalTime(&t,GMT_TO_LOCAL)); +#elif defined(AMPM) + strftime(TimeBuf,100,"%D %r",LocalTime(&t,GMT_TO_LOCAL)); +#elif defined(TZ_TIME) + { + strftime(TimeBuf,100,"%D:%T",LocalTime(&t,0)); + sprintf(TimeBuf+strlen(TimeBuf)," %+03d%02d", + -TimeDiff(t)/(60*60),-(TimeDiff(t)/60)%60); + } +#else + strftime(TimeBuf,100,"%D %T",LocalTime(&t,GMT_TO_LOCAL)); +#endif + return(TimeBuf); +} + +/**************************************************************************** +determine whether we are in the specified group +****************************************************************************/ +BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups) +{ + int i; + + if (group == current_gid) return(True); + + for (i=0;i<ngroups;i++) + if (group == groups[i]) + return(True); + + return(False); +} + +/**************************************************************************** +this is a safer strcpy(), meant to prevent core dumps when nasty things happen +****************************************************************************/ +char *StrCpy(char *dest,char *src) +{ + char *d = dest; + +#if AJT + /* I don't want to get lazy with these ... */ + if (!dest || !src) { + DEBUG(0,("ERROR: NULL StrCpy() called!\n")); + ajt_panic(); + } +#endif + + if (!dest) return(NULL); + if (!src) { + *dest = 0; + return(dest); + } + while ((*d++ = *src++)) ; + return(dest); +} + +/**************************************************************************** +line strncpy but always null terminates. Make sure there is room! +****************************************************************************/ +char *StrnCpy(char *dest,const char *src,int n) +{ + char *d = dest; + if (!dest) return(NULL); + if (!src) { + *dest = 0; + return(dest); + } + while (n-- && (*d++ = *src++)) ; + *d = 0; + return(dest); +} + + +/******************************************************************* +copy an IP address from one buffer to another +********************************************************************/ +void putip(void *dest,void *src) +{ + memcpy(dest,src,4); +} + + +/**************************************************************************** +interpret the weird netbios "name". Return the name type +****************************************************************************/ +static int name_interpret(char *in,char *out) +{ + int ret; + int len = (*in++) / 2; + + *out=0; + + if (len > 30 || len<1) return(0); + + while (len--) + { + if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { + *out = 0; + return(0); + } + *out = ((in[0]-'A')<<4) + (in[1]-'A'); + in += 2; + out++; + } + *out = 0; + ret = out[-1]; + +#ifdef NETBIOS_SCOPE + /* Handle any scope names */ + while(*in) + { + *out++ = '.'; /* Scope names are separated by periods */ + len = *(unsigned char *)in++; + StrnCpy(out, in, len); + out += len; + *out=0; + in += len; + } +#endif + return(ret); +} + +/**************************************************************************** +mangle a name into netbios format +****************************************************************************/ +int name_mangle(char *In,char *Out,char name_type) +{ + fstring name; + char buf[20]; + char *in = (char *)&buf[0]; + char *out = (char *)Out; + char *p, *label; + int i; + + if (In[0] != '*') { + StrnCpy(name,In,sizeof(name)-1); + sprintf(buf,"%-15.15s%c",name,name_type); + } else { + buf[0]='*'; + memset(&buf[1],0,16); + } + + *out++ = 32; + for (i=0;i<16;i++) { + char c = toupper(in[i]); + out[i*2] = (c>>4) + 'A'; + out[i*2+1] = (c & 0xF) + 'A'; + } + out[32]=0; + out += 32; + + label = scope; + while (*label) + { + p = strchr(label, '.'); + if (p == 0) + p = label + strlen(label); + *out++ = p - label; + memcpy(out, label, p - label); + out += p - label; + label += p - label + (*p == '.'); + } + *out = 0; + return(name_len(Out)); +} + + +/******************************************************************* + check if a file exists +********************************************************************/ +BOOL file_exist(char *fname,struct stat *sbuf) +{ + struct stat st; + if (!sbuf) sbuf = &st; + + if (sys_stat(fname,sbuf) != 0) + return(False); + + return(S_ISREG(sbuf->st_mode)); +} + +/******************************************************************* +check a files mod time +********************************************************************/ +time_t file_modtime(char *fname) +{ + struct stat st; + + if (sys_stat(fname,&st) != 0) + return(0); + + return(st.st_mtime); +} + +/******************************************************************* + check if a directory exists +********************************************************************/ +BOOL directory_exist(char *dname,struct stat *st) +{ + struct stat st2; + if (!st) st = &st2; + + if (sys_stat(dname,st) != 0) + return(False); + + return(S_ISDIR(st->st_mode)); +} + +/******************************************************************* +returns the size in bytes of the named file +********************************************************************/ +uint32 file_size(char *file_name) +{ + struct stat buf; + buf.st_size = 0; + sys_stat(file_name,&buf); + return(buf.st_size); +} + +/**************************************************************************** +check if it's a null mtime +****************************************************************************/ +static BOOL null_mtime(time_t mtime) +{ + if (mtime == 0 || mtime == 0xFFFFFFFF) + return(True); + return(False); +} + +/******************************************************************* + create a 16 bit dos packed date +********************************************************************/ +static uint16 make_dos_date1(time_t unixdate,struct tm *t) +{ + uint16 ret=0; + ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1); + ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5)); + return(ret); +} + +/******************************************************************* + create a 16 bit dos packed time +********************************************************************/ +static uint16 make_dos_time1(time_t unixdate,struct tm *t) +{ + uint16 ret=0; + ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3)); + ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5)); + return(ret); +} + +/******************************************************************* + create a 32 bit dos packed date/time from some parameters + This takes a GMT time and returns a packed localtime structure +********************************************************************/ +static uint32 make_dos_date(time_t unixdate) +{ + struct tm *t; + uint32 ret=0; + + t = LocalTime(&unixdate,GMT_TO_LOCAL); + + ret = make_dos_date1(unixdate,t); + ret = ((ret&0xFFFF)<<16) | make_dos_time1(unixdate,t); + + return(ret); +} + +/******************************************************************* +put a dos date into a buffer (time/date format) +This takes GMT time and puts local time in the buffer +********************************************************************/ +void put_dos_date(char *buf,int offset,time_t unixdate) +{ + uint32 x = make_dos_date(unixdate); + SIVAL(buf,offset,x); +} + +/******************************************************************* +put a dos date into a buffer (date/time format) +This takes GMT time and puts local time in the buffer +********************************************************************/ +void put_dos_date2(char *buf,int offset,time_t unixdate) +{ + uint32 x = make_dos_date(unixdate); + x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(buf,offset,x); +} + +/******************************************************************* +put a dos 32 bit "unix like" date into a buffer. This routine takes +GMT and converts it to LOCAL time before putting it (most SMBs assume +localtime for this sort of date) +********************************************************************/ +void put_dos_date3(char *buf,int offset,time_t unixdate) +{ + if (!null_mtime(unixdate)) + unixdate += GMT_TO_LOCAL*TimeDiff(unixdate); + SIVAL(buf,offset,unixdate); +} + +/******************************************************************* + interpret a 32 bit dos packed date/time to some parameters +********************************************************************/ +static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second) +{ + uint32 p0,p1,p2,p3; + + p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; + p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF; + + *second = 2*(p0 & 0x1F); + *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3); + *hour = (p1>>3)&0xFF; + *day = (p2&0x1F); + *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1; + *year = ((p3>>1)&0xFF) + 80; +} + +/******************************************************************* + create a unix date (int GMT) from a dos date (which is actually in + localtime) +********************************************************************/ +time_t make_unix_date(void *date_ptr) +{ + uint32 dos_date=0; + struct tm t; + time_t ret; + + dos_date = IVAL(date_ptr,0); + + if (dos_date == 0) return(0); + + interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, + &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); + t.tm_wday = 1; + t.tm_yday = 1; + t.tm_isdst = -1; + + /* mktime() also does the local to GMT time conversion for us. XXXXX + Do all unixes do this the same?? */ + ret = mktime(&t); + + return(ret); +} + +/******************************************************************* +like make_unix_date() but the words are reversed +********************************************************************/ +time_t make_unix_date2(void *date_ptr) +{ + uint32 x,x2; + + x = IVAL(date_ptr,0); + x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(&x,0,x2); + + return(make_unix_date((void *)&x)); +} + +/******************************************************************* + create a unix GMT date from a dos date in 32 bit "unix like" format +these generally arrive as localtimes, with corresponding DST +********************************************************************/ +time_t make_unix_date3(void *date_ptr) +{ + time_t t = IVAL(date_ptr,0); + if (!null_mtime(t)) + t += LOCAL_TO_GMT*TimeDiff(t); + return(t); +} + +/******************************************************************* +return a string representing an attribute for a file +********************************************************************/ +char *attrib_string(int mode) +{ + static char attrstr[10]; + + attrstr[0] = 0; + + if (mode & aVOLID) strcat(attrstr,"V"); + if (mode & aDIR) strcat(attrstr,"D"); + if (mode & aARCH) strcat(attrstr,"A"); + if (mode & aHIDDEN) strcat(attrstr,"H"); + if (mode & aSYSTEM) strcat(attrstr,"S"); + if (mode & aRONLY) strcat(attrstr,"R"); + + return(attrstr); +} + + +/******************************************************************* + case insensitive string compararison +********************************************************************/ +int StrCaseCmp(char *s, char *t) +{ + for (; tolower(*s) == tolower(*t); ++s, ++t) + if (!*s) return 0; + + return tolower(*s) - tolower(*t); +} + +/******************************************************************* + case insensitive string compararison, length limited +********************************************************************/ +int StrnCaseCmp(char *s, char *t, int n) +{ + while (n-- && *s && *t) { + if (tolower(*s) != tolower(*t)) return(tolower(*s) - tolower(*t)); + s++; t++; + } + if (n) return(tolower(*s) - tolower(*t)); + + return(0); +} + +/******************************************************************* + compare 2 strings +********************************************************************/ +BOOL strequal(char *s1,char *s2) +{ + if (s1 == s2) return(True); + if (!s1 || !s2) return(False); + + return(StrCaseCmp(s1,s2)==0); +} + +/******************************************************************* + compare 2 strings up to and including the nth char. + ******************************************************************/ +BOOL strnequal(char *s1,char *s2,int n) +{ + if (s1 == s2) return(True); + if (!s1 || !s2 || !n) return(False); + + return(StrnCaseCmp(s1,s2,n)==0); +} + +/******************************************************************* + compare 2 strings (case sensitive) +********************************************************************/ +BOOL strcsequal(char *s1,char *s2) +{ + if (s1 == s2) return(True); + if (!s1 || !s2) return(False); + + return(strcmp(s1,s2)==0); +} + + +/******************************************************************* + convert a string to lower case +********************************************************************/ +void strlower(char *s) +{ + while (*s) + { +#ifdef KANJI + if (is_shift_jis (*s)) { + s += 2; + } else if (is_kana (*s)) { + s++; + } else { + if (isupper(*s)) + *s = tolower(*s); + s++; + } +#else + if (isupper(*s)) + *s = tolower(*s); + s++; +#endif /* KANJI */ + } +} + +/******************************************************************* + convert a string to upper case +********************************************************************/ +void strupper(char *s) +{ + while (*s) + { +#ifdef KANJI + if (is_shift_jis (*s)) { + s += 2; + } else if (is_kana (*s)) { + s++; + } else { + if (islower(*s)) + *s = toupper(*s); + s++; + } +#else + if (islower(*s)) + *s = toupper(*s); + s++; +#endif + } +} + +/******************************************************************* + convert a string to "normal" form +********************************************************************/ +void strnorm(char *s) +{ + if (case_default == CASE_UPPER) + strupper(s); + else + strlower(s); +} + +/******************************************************************* +check if a string is in "normal" case +********************************************************************/ +BOOL strisnormal(char *s) +{ + if (case_default == CASE_UPPER) + return(!strhaslower(s)); + + return(!strhasupper(s)); +} + + +/**************************************************************************** + string replace +****************************************************************************/ +void string_replace(char *s,char oldc,char newc) +{ + while (*s) + { +#ifdef KANJI + if (is_shift_jis (*s)) { + s += 2; + } else if (is_kana (*s)) { + s++; + } else { + if (oldc == *s) + *s = newc; + s++; + } +#else + if (oldc == *s) + *s = newc; + s++; +#endif /* KANJI */ + } +} + +/**************************************************************************** + make a file into unix format +****************************************************************************/ +void unix_format(char *fname) +{ + pstring namecopy; + string_replace(fname,'\\','/'); +#ifndef KANJI + dos2unix_format(fname, True); +#endif /* KANJI */ + + if (*fname == '/') + { + strcpy(namecopy,fname); + strcpy(fname,"."); + strcat(fname,namecopy); + } +} + +/**************************************************************************** + make a file into dos format +****************************************************************************/ +void dos_format(char *fname) +{ +#ifndef KANJI + unix2dos_format(fname, True); +#endif /* KANJI */ + string_replace(fname,'/','\\'); +} + + +/******************************************************************* + show a smb message structure +********************************************************************/ +void show_msg(char *buf) +{ + int i; + int bcc=0; + if (DEBUGLEVEL < 5) + return; + + DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n", + smb_len(buf), + (int)CVAL(buf,smb_com), + (int)CVAL(buf,smb_rcls), + (int)CVAL(buf,smb_reh), + (int)SVAL(buf,smb_err), + (int)CVAL(buf,smb_flg), + (int)SVAL(buf,smb_flg2))); + DEBUG(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\nsmt_wct=%d\n", + (int)SVAL(buf,smb_tid), + (int)SVAL(buf,smb_pid), + (int)SVAL(buf,smb_uid), + (int)SVAL(buf,smb_mid), + (int)CVAL(buf,smb_wct))); + for (i=0;i<(int)CVAL(buf,smb_wct);i++) + DEBUG(5,("smb_vwv[%d]=%d (0x%X)\n",i, + SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i))); + bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct))); + DEBUG(5,("smb_bcc=%d\n",bcc)); + if (DEBUGLEVEL < 10) + return; + for (i=0;i<MIN(bcc,128);i++) + DEBUG(10,("%X ",CVAL(smb_buf(buf),i))); + DEBUG(10,("\n")); +} + +/******************************************************************* + return the length of an smb packet +********************************************************************/ +int smb_len(char *buf) +{ + return( PVAL(buf,3) | (PVAL(buf,2)<<8) | ((PVAL(buf,1)&1)<<16) ); +} + +/******************************************************************* + set the length of an smb packet +********************************************************************/ +void _smb_setlen(char *buf,int len) +{ + buf[0] = 0; + buf[1] = (len&0x10000)>>16; + buf[2] = (len&0xFF00)>>8; + buf[3] = len&0xFF; +} + +/******************************************************************* + set the length and marker of an smb packet +********************************************************************/ +void smb_setlen(char *buf,int len) +{ + _smb_setlen(buf,len); + + CVAL(buf,4) = 0xFF; + CVAL(buf,5) = 'S'; + CVAL(buf,6) = 'M'; + CVAL(buf,7) = 'B'; +} + +/******************************************************************* + setup the word count and byte count for a smb message +********************************************************************/ +int set_message(char *buf,int num_words,int num_bytes,BOOL zero) +{ + if (zero) + bzero(buf + smb_size,num_words*2 + num_bytes); + CVAL(buf,smb_wct) = num_words; + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + return (smb_size + num_words*2 + num_bytes); +} + +/******************************************************************* +return the number of smb words +********************************************************************/ +int smb_numwords(char *buf) +{ + return (CVAL(buf,smb_wct)); +} + +/******************************************************************* +return the size of the smb_buf region of a message +********************************************************************/ +int smb_buflen(char *buf) +{ + return(SVAL(buf,smb_vwv0 + smb_numwords(buf)*2)); +} + +/******************************************************************* + return a pointer to the smb_buf data area +********************************************************************/ +int smb_buf_ofs(char *buf) +{ + return (smb_size + CVAL(buf,smb_wct)*2); +} + +/******************************************************************* + return a pointer to the smb_buf data area +********************************************************************/ +char *smb_buf(char *buf) +{ + return (buf + smb_buf_ofs(buf)); +} + +/******************************************************************* +return the SMB offset into an SMB buffer +********************************************************************/ +int smb_offset(char *p,char *buf) +{ + return(PTR_DIFF(p,buf+4)); +} + + +/******************************************************************* +skip past some strings in a buffer +********************************************************************/ +char *skip_string(char *buf,int n) +{ + while (n--) + buf += strlen(buf) + 1; + return(buf); +} + +/******************************************************************* +trim the specified elements off the front and back of a string +********************************************************************/ +BOOL trim_string(char *s,char *front,char *back) +{ + BOOL ret = False; + while (front && *front && strncmp(s,front,strlen(front)) == 0) + { + char *p = s; + ret = True; + while (1) + { + if (!(*p = p[strlen(front)])) + break; + p++; + } + } + while (back && *back && strlen(s) >= strlen(back) && + (strncmp(s+strlen(s)-strlen(back),back,strlen(back))==0)) + { + ret = True; + s[strlen(s)-strlen(back)] = 0; + } + return(ret); +} + + +/******************************************************************* +reduce a file name, removing .. elements. +********************************************************************/ +void dos_clean_name(char *s) +{ + char *p=NULL; + + DEBUG(3,("dos_clean_name [%s]\n",s)); + + /* remove any double slashes */ + string_sub(s, "\\\\", "\\"); + + while ((p = strstr(s,"\\..\\")) != NULL) + { + pstring s1; + + *p = 0; + strcpy(s1,p+3); + + if ((p=strrchr(s,'\\')) != NULL) + *p = 0; + else + *s = 0; + strcat(s,s1); + } + + trim_string(s,NULL,"\\.."); + + string_sub(s, "\\.\\", "\\"); +} + +/******************************************************************* +reduce a file name, removing .. elements. +********************************************************************/ +void unix_clean_name(char *s) +{ + char *p=NULL; + + DEBUG(3,("unix_clean_name [%s]\n",s)); + + /* remove any double slashes */ + string_sub(s, "//","/"); + + while ((p = strstr(s,"/../")) != NULL) + { + pstring s1; + + *p = 0; + strcpy(s1,p+3); + + if ((p=strrchr(s,'/')) != NULL) + *p = 0; + else + *s = 0; + strcat(s,s1); + } + + trim_string(s,NULL,"/.."); +} + + +/******************************************************************* +a wrapper for the normal chdir() function +********************************************************************/ +int ChDir(char *path) +{ + int res; + static pstring LastDir=""; + + if (strcsequal(path,".")) return(0); + + if (*path == '/' && strcsequal(LastDir,path)) return(0); + DEBUG(3,("chdir to %s\n",path)); + res = sys_chdir(path); + if (!res) + strcpy(LastDir,path); + return(res); +} + + +/******************************************************************* + return the absolute current directory path. A dumb version. +********************************************************************/ +static char *Dumb_GetWd(char *s) +{ +#ifdef USE_GETCWD + return ((char *)getcwd(s,sizeof(pstring))); +#else + return ((char *)getwd(s)); +#endif +} + + +/* number of list structures for a caching GetWd function. */ +#define MAX_GETWDCACHE (50) + +struct +{ + ino_t inode; + dev_t dev; + char *text; + BOOL valid; +} ino_list[MAX_GETWDCACHE]; + +BOOL use_getwd_cache=True; + +/******************************************************************* + return the absolute current directory path +********************************************************************/ +char *GetWd(char *str) +{ + pstring s; + static BOOL getwd_cache_init = False; + struct stat st, st2; + int i; + + *s = 0; + + if (!use_getwd_cache) + return(Dumb_GetWd(str)); + + /* init the cache */ + if (!getwd_cache_init) + { + getwd_cache_init = True; + for (i=0;i<MAX_GETWDCACHE;i++) + { + string_init(&ino_list[i].text,""); + ino_list[i].valid = False; + } + } + + /* Get the inode of the current directory, if this doesn't work we're + in trouble :-) */ + + if (stat(".",&st) == -1) + { + DEBUG(0,("Very strange, couldn't stat \".\"\n")); + return(Dumb_GetWd(str)); + } + + + for (i=0; i<MAX_GETWDCACHE; i++) + if (ino_list[i].valid) + { + + /* If we have found an entry with a matching inode and dev number + then find the inode number for the directory in the cached string. + If this agrees with that returned by the stat for the current + directory then all is o.k. (but make sure it is a directory all + the same...) */ + + if (st.st_ino == ino_list[i].inode && + st.st_dev == ino_list[i].dev) + { + if (stat(ino_list[i].text,&st2) == 0) + { + if (st.st_ino == st2.st_ino && + st.st_dev == st2.st_dev && + (st2.st_mode & S_IFMT) == S_IFDIR) + { + strcpy (str, ino_list[i].text); + + /* promote it for future use */ + array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); + return (str); + } + else + { + /* If the inode is different then something's changed, + scrub the entry and start from scratch. */ + ino_list[i].valid = False; + } + } + } + } + + + /* We don't have the information to hand so rely on traditional methods. + The very slow getcwd, which spawns a process on some systems, or the + not quite so bad getwd. */ + + if (!Dumb_GetWd(s)) + { + DEBUG(0,("Getwd failed, errno %d\n",errno)); + return (NULL); + } + + strcpy(str,s); + + DEBUG(5,("GetWd %s, inode %d, dev %x\n",s,(int)st.st_ino,(int)st.st_dev)); + + /* add it to the cache */ + i = MAX_GETWDCACHE - 1; + string_set(&ino_list[i].text,s); + ino_list[i].dev = st.st_dev; + ino_list[i].inode = st.st_ino; + ino_list[i].valid = True; + + /* put it at the top of the list */ + array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); + + return (str); +} + + + +/******************************************************************* +reduce a file name, removing .. elements and checking that +it is below dir in the heirachy. This uses GetWd() and so must be run +on the system that has the referenced file system. + +widelinks are allowed if widelinks is true +********************************************************************/ +BOOL reduce_name(char *s,char *dir,BOOL widelinks) +{ +#ifndef REDUCE_PATHS + return True; +#else + pstring dir2; + pstring wd; + pstring basename; + pstring newname; + char *p=NULL; + BOOL relative = (*s != '/'); + + *dir2 = *wd = *basename = *newname = 0; + + if (widelinks) + { + unix_clean_name(s); + /* can't have a leading .. */ + if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) + { + DEBUG(3,("Illegal file name? (%s)\n",s)); + return(False); + } + return(True); + } + + DEBUG(3,("reduce_name [%s] [%s]\n",s,dir)); + + /* remove any double slashes */ + string_sub(s,"//","/"); + + strcpy(basename,s); + p = strrchr(basename,'/'); + + if (!p) + return(True); + + if (!GetWd(wd)) + { + DEBUG(0,("couldn't getwd for %s %s\n",s,dir)); + return(False); + } + + if (ChDir(dir) != 0) + { + DEBUG(0,("couldn't chdir to %s\n",dir)); + return(False); + } + + if (!GetWd(dir2)) + { + DEBUG(0,("couldn't getwd for %s\n",dir)); + ChDir(wd); + return(False); + } + + + if (p && (p != basename)) + { + *p = 0; + if (strcmp(p+1,".")==0) + p[1]=0; + if (strcmp(p+1,"..")==0) + *p = '/'; + } + + if (ChDir(basename) != 0) + { + ChDir(wd); + DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,basename)); + return(False); + } + + if (!GetWd(newname)) + { + ChDir(wd); + DEBUG(2,("couldn't get wd for %s %s\n",s,dir2)); + return(False); + } + + if (p && (p != basename)) + { + strcat(newname,"/"); + strcat(newname,p+1); + } + + { + int l = strlen(dir2); + if (dir2[l-1] == '/') + l--; + + if (strncmp(newname,dir2,l) != 0) + { + ChDir(wd); + DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,l)); + return(False); + } + + if (relative) + { + if (newname[l] == '/') + strcpy(s,newname + l + 1); + else + strcpy(s,newname+l); + } + else + strcpy(s,newname); + } + + ChDir(wd); + + if (strlen(s) == 0) + strcpy(s,"./"); + + DEBUG(3,("reduced to %s\n",s)); + return(True); +#endif +} + +/**************************************************************************** +expand some *s +****************************************************************************/ +static void expand_one(char *Mask,int len) +{ + char *p1; + while ((p1 = strchr(Mask,'*')) != NULL) + { + int lfill = (len+1) - strlen(Mask); + int l1= (p1 - Mask); + pstring tmp; + strcpy(tmp,Mask); + memset(tmp+l1,'?',lfill); + strcpy(tmp + l1 + lfill,Mask + l1 + 1); + strcpy(Mask,tmp); + } +} + +/**************************************************************************** +expand a wildcard expression, replacing *s with ?s +****************************************************************************/ +void expand_mask(char *Mask,BOOL doext) +{ + pstring mbeg,mext; + pstring dirpart; + pstring filepart; + BOOL hasdot = False; + char *p1; + BOOL absolute = (*Mask == '\\'); + + *mbeg = *mext = *dirpart = *filepart = 0; + + /* parse the directory and filename */ + if (strchr(Mask,'\\')) + dirname_dos(Mask,dirpart); + + filename_dos(Mask,filepart); + + strcpy(mbeg,filepart); + if ((p1 = strchr(mbeg,'.')) != NULL) + { + hasdot = True; + *p1 = 0; + p1++; + strcpy(mext,p1); + } + else + { + strcpy(mext,""); + if (strlen(mbeg) > 8) + { + strcpy(mext,mbeg + 8); + mbeg[8] = 0; + } + } + + if (*mbeg == 0) + strcpy(mbeg,"????????"); + if ((*mext == 0) && doext && !hasdot) + strcpy(mext,"???"); + + if (strequal(mbeg,"*") && *mext==0) + strcpy(mext,"*"); + + /* expand *'s */ + expand_one(mbeg,8); + if (*mext) + expand_one(mext,3); + + strcpy(Mask,dirpart); + if (*dirpart || absolute) strcat(Mask,"\\"); + strcat(Mask,mbeg); + strcat(Mask,"."); + strcat(Mask,mext); + + DEBUG(6,("Mask expanded to [%s]\n",Mask)); +} + + +/**************************************************************************** +does a string have any uppercase chars in it? +****************************************************************************/ +BOOL strhasupper(char *s) +{ + while (*s) + { +#ifdef KANJI + if (is_shift_jis (*s)) { + s += 2; + } else if (is_kana (*s)) { + s++; + } else { + if (isupper(*s)) return(True); + s++; + } +#else + if (isupper(*s)) return(True); + s++; +#endif /* KANJI */ + } + return(False); +} + +/**************************************************************************** +does a string have any lowercase chars in it? +****************************************************************************/ +BOOL strhaslower(char *s) +{ + while (*s) + { +#ifdef KANJI + if (is_shift_jis (*s)) { + s += 2; + } else if (is_kana (*s)) { + s++; + } else { + if (islower(*s)) return(True); + s++; + } +#else + if (islower(*s)) return(True); + s++; +#endif /* KANJI */ + } + return(False); +} + +/**************************************************************************** +find the number of chars in a string +****************************************************************************/ +int count_chars(char *s,char c) +{ + int count=0; + while (*s) + { + if (*s == c) + count++; + s++; + } + return(count); +} + + +/**************************************************************************** + make a dir struct +****************************************************************************/ +void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date) +{ + char *p; + pstring mask2; + + strcpy(mask2,mask); + + if ((mode & aDIR) != 0) + size = 0; + + memset(buf+1,' ',11); + if ((p = strchr(mask2,'.')) != NULL) + { + *p = 0; + memcpy(buf+1,mask2,MIN(strlen(mask2),8)); + memcpy(buf+9,p+1,MIN(strlen(p+1),3)); + *p = '.'; + } + else + memcpy(buf+1,mask2,MIN(strlen(mask2),11)); + + bzero(buf+21,DIR_STRUCT_SIZE-21); + CVAL(buf,21) = mode; + put_dos_date(buf,22,date); + SSVAL(buf,26,size & 0xFFFF); + SSVAL(buf,28,size >> 16); + StrnCpy(buf+30,fname,12); + if (!case_sensitive) + strupper(buf+30); + DEBUG(8,("put name [%s] into dir struct\n",buf+30)); +} + + +/******************************************************************* +close the low 3 fd's and open dev/null in their place +********************************************************************/ +void close_low_fds(void) +{ + int fd; + int i; + close(0); close(1); close(2); + /* try and use up these file descriptors, so silly + library routines writing to stdout etc won't cause havoc */ + for (i=0;i<3;i++) { + fd = open("/dev/null",O_RDWR,0); + if (fd < 0) fd = open("/dev/null",O_WRONLY,0); + if (fd < 0) { + DEBUG(0,("Can't open /dev/null\n")); + return; + } + if (fd != i) { + DEBUG(0,("Didn't get file descriptor %d\n",i)); + return; + } + } +} + + +/**************************************************************************** +write to a socket +****************************************************************************/ +int write_socket(int fd,char *buf,int len) +{ + int ret=0; + + if (passive) + return(len); + DEBUG(6,("write_socket(%d,%d)\n",fd,len)); + ret = write_data(fd,buf,len); + + DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,len,ret)); + return(ret); +} + +/**************************************************************************** +read from a socket +****************************************************************************/ +int read_udp_socket(int fd,char *buf,int len) +{ + int ret; + struct sockaddr sock; + int socklen; + + socklen = sizeof(sock); + bzero((char *)&sock,socklen); + bzero((char *)&lastip,sizeof(lastip)); + ret = recvfrom(fd,buf,len,0,&sock,&socklen); + if (ret <= 0) + { + DEBUG(2,("read socket failed. ERRNO=%d\n",errno)); + return(0); + } + + lastip = *(struct in_addr *) &sock.sa_data[2]; + lastport = ntohs(((struct sockaddr_in *)&sock)->sin_port); + + return(ret); +} + +/**************************************************************************** +Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, +else +if SYSV use O_NDELAY +if BSD use FNDELAY +****************************************************************************/ +int set_blocking(int fd, BOOL set) +{ +int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = fcntl(fd, F_GETFL, 0))==-1) + return -1; + if(set) /* Turn blocking on - ie. clear nonblock flag */ + val &= ~FLAG_TO_SET; + else + val |= FLAG_TO_SET; + return fcntl( fd, F_SETFL, val); +#undef FLAG_TO_SET +} + + +/**************************************************************************** +Calculate the difference in timeout values. Return 1 if val1 > val2, +0 if val1 == val2, -1 if val1 < val2. Stores result in retval. retval +may be == val1 or val2 +****************************************************************************/ +static int tval_sub( struct timeval *retval, struct timeval *val1, struct timeval *val2) +{ + int usecdiff = val1->tv_usec - val2->tv_usec; + int secdiff = val1->tv_sec - val2->tv_sec; + if(usecdiff < 0) { + usecdiff = 1000000 + usecdiff; + secdiff--; + } + retval->tv_sec = secdiff; + retval->tv_usec = usecdiff; + if(secdiff < 0) + return -1; + if(secdiff > 0) + return 1; + return (usecdiff < 0 ) ? -1 : ((usecdiff > 0 ) ? 1 : 0); +} + +/**************************************************************************** +read data from a device with a timout in msec. +mincount = if timeout, minimum to read before returning +maxcount = number to be read. +****************************************************************************/ +int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt,long time_out,BOOL exact) +{ + fd_set fds; + int selrtn; + int readret; + int nread = 0; + struct timeval timeout, tval1, tval2, tvaldiff; + int error_limit = 5; + + /* just checking .... */ + if (maxcnt <= 0) return(0); + + if(time_out == -2) + time_out = DEFAULT_PIPE_TIMEOUT; + + /* Blocking read */ + if(time_out < 0) { + if (mincnt == 0) mincnt = maxcnt; + + while (nread < mincnt) + { + readret = read(fd, buf + nread, maxcnt - nread); + if (readret <= 0) return(nread); + nread += readret; + } + return(nread); + } + + /* Non blocking read */ + if(time_out == 0) { + set_blocking(fd, False); + nread = read_data(fd, buf, mincnt); + if (nread < maxcnt) + nread += read(fd,buf+nread,maxcnt-nread); + if(nread == -1 && errno == EWOULDBLOCK) + nread = 0; + set_blocking(fd,True); + return nread; + } + + /* Most difficult - timeout read */ + /* If this is ever called on a disk file and + mincnt is greater then the filesize then + system performance will suffer severely as + select always return true on disk files */ + + /* Set initial timeout */ + timeout.tv_sec = time_out / 1000; + timeout.tv_usec = 1000 * (time_out % 1000); + + /* As most UNIXes don't modify the value of timeout + when they return from select we need to get the timeofday (in usec) + now, and also after the select returns so we know + how much time has elapsed */ + + if (exact) + GetTimeOfDay( &tval1); + nread = 0; /* Number of bytes we have read */ + + for(;;) + { + FD_ZERO(&fds); + FD_SET(fd,&fds); + + selrtn = sys_select(&fds,&timeout); + + /* Check if error */ + if(selrtn == -1) { + errno = EBADF; + return -1; + } + + /* Did we timeout ? */ + if (selrtn == 0) { + if (nread < mincnt) return -1; + break; /* Yes */ + } + + readret = read(fd, buf+nread, maxcnt-nread); + if (readret == 0 && nread < mincnt) { + /* error_limit should not really be needed, but some systems + do strange things ... I don't want to just continue + indefinately in case we get an infinite loop */ + if (error_limit--) continue; + return(-1); + } + + if (readret < 0) { + /* force a particular error number for + portability */ + DEBUG(5,("read gave error %s\n",strerror(errno))); + errno = EBADF; + return -1; + } + + nread += readret; + + /* If we have read more than mincnt then return */ + if (nread >= mincnt) + break; + + /* We need to do another select - but first reduce the + time_out by the amount of time already elapsed - if + this is less than zero then return */ + if (exact) { + GetTimeOfDay(&tval2); + (void)tval_sub( &tvaldiff, &tval2, &tval1); + + if (tval_sub(&timeout, &timeout, &tvaldiff) <= 0) + break; /* We timed out */ + } + + /* Save the time of day as we need to do the select + again (saves a system call) */ + tval1 = tval2; + } + + /* Return the number we got */ + return(nread); +} + +/**************************************************************************** +read data from the client. Maxtime is in milliseconds +****************************************************************************/ +int read_max_udp(int fd,char *buffer,int bufsize,int maxtime) +{ + fd_set fds; + int selrtn; + int nread; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + timeout.tv_sec = maxtime / 1000; + timeout.tv_usec = (maxtime % 1000) * 1000; + + selrtn = sys_select(&fds,maxtime>0?&timeout:NULL); + + if (!FD_ISSET(fd,&fds)) + return 0; + + nread = read_udp_socket(fd, buffer, bufsize); + + /* return the number got */ + return(nread); +} + +/******************************************************************* +find the difference in milliseconds between two struct timeval +values +********************************************************************/ +int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew) +{ + return((tvalnew->tv_sec - tvalold->tv_sec)*1000 + + ((int)tvalnew->tv_usec - (int)tvalold->tv_usec)/1000); +} + +/**************************************************************************** +send a keepalive packet (rfc1002) +****************************************************************************/ +BOOL send_keepalive(int client) +{ + unsigned char buf[4]; + + buf[0] = 0x85; + buf[1] = buf[2] = buf[3] = 0; + + return(write_data(client,(char *)buf,4) == 4); +} + + + +/**************************************************************************** + read data from the client, reading exactly N bytes. +****************************************************************************/ +int read_data(int fd,char *buffer,int N) +{ + int ret; + int total=0; + + while (total < N) + { + ret = read(fd,buffer + total,N - total); + + /* this is for portability */ + if (ret < 0) + errno = EBADF; + + if (ret <= 0) + return total; + total += ret; + } + return total; +} + + +/**************************************************************************** + write data to a fd +****************************************************************************/ +int write_data(int fd,char *buffer,int N) +{ + int total=0; + int ret; + + while (total < N) + { + ret = write(fd,buffer + total,N - total); + + if (ret <= 0) + return total; + + total += ret; + } + return total; +} + + +/* variables used by the read prediction module */ +int rp_fd = -1; +int rp_offset = 0; +int rp_length = 0; +int rp_alloced = 0; +int rp_predict_fd = -1; +int rp_predict_offset = 0; +int rp_predict_length = 0; +int rp_timeout = 5; +time_t rp_time = 0; +char *rp_buffer = NULL; +BOOL predict_skip=False; +time_t smb_last_time=(time_t)0; + +/**************************************************************************** +handle read prediction on a file +****************************************************************************/ +int read_predict(int fd,int offset,char *buf,char **ptr,int num) +{ + int ret = 0; + int possible = rp_length - (offset - rp_offset); + + possible = MIN(possible,num); + + /* give data if possible */ + if (fd == rp_fd && + offset >= rp_offset && + possible>0 && + smb_last_time-rp_time < rp_timeout) + { + ret = possible; + if (buf) + memcpy(buf,rp_buffer + (offset-rp_offset),possible); + else + *ptr = rp_buffer + (offset-rp_offset); + DEBUG(5,("read-prediction gave %d bytes of %d\n",ret,num)); + } + + if (ret == num) { + predict_skip = True; + } else { + predict_skip = False; + + /* prepare the next prediction */ + rp_predict_fd = fd; + rp_predict_offset = offset + num; + rp_predict_length = num; + } + + if (ret < 0) ret = 0; + + return(ret); +} + +/**************************************************************************** +pre-read some data +****************************************************************************/ +void do_read_prediction() +{ + if (predict_skip) return; + + if (rp_predict_fd == -1) + return; + + rp_fd = rp_predict_fd; + rp_offset = rp_predict_offset; + rp_length = 0; + + rp_predict_fd = -1; + + rp_predict_length = MIN(rp_predict_length,2*ReadSize); + rp_predict_length = MAX(rp_predict_length,1024); + rp_offset = (rp_offset/1024)*1024; + rp_predict_length = (rp_predict_length/1024)*1024; + + if (rp_predict_length > rp_alloced) + { + rp_buffer = Realloc(rp_buffer,rp_predict_length); + rp_alloced = rp_predict_length; + if (!rp_buffer) + { + DEBUG(0,("can't allocate read-prediction buffer\n")); + rp_predict_fd = -1; + rp_fd = -1; + rp_alloced = 0; + return; + } + } + + if (lseek(rp_fd,rp_offset,SEEK_SET) != rp_offset) { + rp_fd = -1; + rp_predict_fd = -1; + return; + } + + rp_length = read(rp_fd,rp_buffer,rp_predict_length); + rp_time = time(NULL); + if (rp_length < 0) + rp_length = 0; +} + +/**************************************************************************** +invalidate read-prediction on a fd +****************************************************************************/ +void invalidate_read_prediction(int fd) +{ + if (rp_fd == fd) + rp_fd = -1; + if (rp_predict_fd == fd) + rp_predict_fd = -1; +} + + +/**************************************************************************** +transfer some data between two fd's +****************************************************************************/ +int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align) +{ + static char *buf=NULL; + char *buf1,*abuf; + static int size = 0; + int total = 0; + + DEBUG(4,("transfer_file %d (head=%d) called\n",n,headlen)); + + if ((size < ReadSize) && buf) { + free(buf); + buf = NULL; + } + + size = MAX(ReadSize,1024); + + while (!buf && size>0) { + buf = (char *)Realloc(buf,size+8); + if (!buf) size /= 2; + } + if (!buf) { + DEBUG(0,("Can't allocate transfer buffer!\n")); + exit(1); + } + + abuf = buf + (align%8); + + if (header) + n += headlen; + + while (n > 0) + { + int s = MIN(n,size); + int ret,ret2=0; + + ret = 0; + + if (header && (headlen >= MIN(s,1024))) { + buf1 = header; + s = headlen; + ret = headlen; + headlen = 0; + header = NULL; + } else { + buf1 = abuf; + } + + if (header && headlen > 0) + { + ret = MIN(headlen,size); + memcpy(buf1,header,ret); + headlen -= ret; + header += ret; + if (headlen <= 0) header = NULL; + } + + if (s > ret) + ret += read(infd,buf1+ret,s-ret); + + if (ret > 0) + { + ret2 = (outfd>=0?write_data(outfd,buf1,ret):ret); + if (ret2 > 0) total += ret2; + /* if we can't write then dump excess data */ + if (ret2 != ret) + transfer_file(infd,-1,n-(ret+headlen),NULL,0,0); + } + if (ret <= 0 || ret2 != ret) + return(total); + n -= ret; + } + return(total); +} + + +/**************************************************************************** +read 4 bytes of a smb packet and return the smb length of the packet +possibly store the result in the buffer +****************************************************************************/ +int read_smb_length(int fd,char *inbuf,int timeout) +{ + char *buffer; + char buf[4]; + int len=0, msg_type; + BOOL ok=False; + + if (inbuf) + buffer = inbuf; + else + buffer = buf; + + while (!ok) + { + if (timeout > 0) + ok = (read_with_timeout(fd,buffer,4,4,timeout,False) == 4); + else + ok = (read_data(fd,buffer,4) == 4); + + if (!ok) + { + if (timeout>0) + { + DEBUG(10,("select timeout (%d)\n", timeout)); + return(-1); + } + else + { + DEBUG(6,("couldn't read from client\n")); + exit(1); + } + } + + len = smb_len(buffer); + msg_type = CVAL(buffer,0); + + if (msg_type == 0x85) + { + DEBUG(5,( "Got keepalive packet\n")); + ok = False; + } + } + + DEBUG(10,("got smb length of %d\n",len)); + + return(len); +} + + + +/**************************************************************************** + read an smb from a fd and return it's length +The timeout is in milli seconds +****************************************************************************/ +BOOL receive_smb(int fd,char *buffer,int timeout) +{ + int len; + BOOL ok; + + bzero(buffer,smb_size + 100); + + len = read_smb_length(fd,buffer,timeout); + if (len == -1) + return(False); + + if (len > BUFFER_SIZE) + { + DEBUG(0,("Invalid packet length! (%d bytes)\n",len)); + if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) + exit(1); + } + + ok = (read_data(fd,buffer+4,len) == len); + + if (!ok) + { + close_sockets(); + exit(1); + } + + return(True); +} + + +/**************************************************************************** + send an smb to a fd +****************************************************************************/ +BOOL send_smb(int fd,char *buffer) +{ + int len; + int ret,nwritten=0; + len = smb_len(buffer) + 4; + + while (nwritten < len) + { + ret = write_socket(fd,buffer+nwritten,len - nwritten); + if (ret <= 0) + { + DEBUG(0,("Error writing %d bytes to client. %d. Exiting\n",len,ret)); + close_sockets(); + exit(1); + } + nwritten += ret; + } + + + return True; +} + + +/**************************************************************************** +find a pointer to a netbios name +****************************************************************************/ +char *name_ptr(char *buf,int ofs) +{ + unsigned char c = *(unsigned char *)(buf+ofs); + + if ((c & 0xC0) == 0xC0) + { + uint16 l; + char p[2]; + memcpy(p,buf+ofs,2); + p[0] &= ~0xC0; + l = RSVAL(p,0); + DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l)); + return(buf + l); + } + else + return(buf+ofs); +} + +/**************************************************************************** +extract a netbios name from a buf +****************************************************************************/ +int name_extract(char *buf,int ofs,char *name) +{ + char *p = name_ptr(buf,ofs); + int d = PTR_DIFF(p,buf+ofs); + strcpy(name,""); + if (d < -50 || d > 50) return(0); + return(name_interpret(p,name)); +} + + +/**************************************************************************** +return the total storage length of a mangled name +****************************************************************************/ +int name_len(char *s) +{ + char *s0=s; + unsigned char c = *(unsigned char *)s; + if ((c & 0xC0) == 0xC0) + return(2); + while (*s) s += (*s)+1; + return(PTR_DIFF(s,s0)+1); +} + +/**************************************************************************** +send a single packet to a port on another machine +****************************************************************************/ +BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type) +{ + BOOL ret; + int out_fd; + struct sockaddr_in sock_out; + + if (passive) + return(True); + + /* create a socket to write to */ + out_fd = socket(AF_INET, type, 0); + if (out_fd == -1) + { + DEBUG(0,("socket failed")); + return False; + } + + /* set the address and port */ + bzero((char *)&sock_out,sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)&ip); + sock_out.sin_port = htons( port ); + sock_out.sin_family = AF_INET; + + if (DEBUGLEVEL > 0) + DEBUG(3,("sending a packet of len %d to (%s) on port %d of type %s\n", + len,inet_ntoa(ip),port,type==SOCK_DGRAM?"DGRAM":"STREAM")); + + /* send it */ + ret = (sendto(out_fd,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0); + + if (!ret) + DEBUG(0,("Packet send to %s(%d) failed ERRNO=%d\n", + inet_ntoa(ip),port,errno)); + + close(out_fd); + return(ret); +} + +/******************************************************************* +sleep for a specified number of milliseconds +********************************************************************/ +void msleep(int t) +{ + int tdiff=0; + struct timeval tval,t1,t2; + fd_set fds; + + GetTimeOfDay(&t1); + GetTimeOfDay(&t2); + + while (tdiff < t) { + tval.tv_sec = (t-tdiff)/1000; + tval.tv_usec = 1000*((t-tdiff)%1000); + + FD_ZERO(&fds); + errno = 0; + sys_select(&fds,&tval); + + GetTimeOfDay(&t2); + tdiff = TvalDiff(&t1,&t2); + } +} + +/**************************************************************************** +check if a string is part of a list +****************************************************************************/ +BOOL in_list(char *s,char *list,BOOL casesensitive) +{ + pstring tok; + char *p=list; + + if (!list) return(False); + + while (next_token(&p,tok,LIST_SEP)) + { + if (casesensitive) { + if (strcmp(tok,s) == 0) + return(True); + } else { + if (StrCaseCmp(tok,s) == 0) + return(True); + } + } + return(False); +} + +/* this is used to prevent lots of mallocs of size 1 */ +static char *null_string = NULL; + +/**************************************************************************** +set a string value, allocing the space for the string +****************************************************************************/ +BOOL string_init(char **dest,char *src) +{ + int l; + if (!src) + src = ""; + + l = strlen(src); + + if (l == 0) + { + if (!null_string) + null_string = (char *)malloc(1); + + *null_string = 0; + *dest = null_string; + } + else + { + *dest = (char *)malloc(l+1); + strcpy(*dest,src); + } + return(True); +} + +/**************************************************************************** +free a string value +****************************************************************************/ +void string_free(char **s) +{ + if (!s || !(*s)) return; + if (*s == null_string) + *s = NULL; + if (*s) free(*s); + *s = NULL; +} + +/**************************************************************************** +set a string value, allocing the space for the string, and deallocating any +existing space +****************************************************************************/ +BOOL string_set(char **dest,char *src) +{ + string_free(dest); + + return(string_init(dest,src)); +} + +/**************************************************************************** +substitute a string for a pattern in another string. Make sure there is +enough room! + +This routine looks for pattern in s and replaces it with +insert. It may do multiple replacements. + +return True if a substitution was done. +****************************************************************************/ +BOOL string_sub(char *s,char *pattern,char *insert) +{ + BOOL ret = False; + char *p; + int ls,lp,li; + + if (!insert || !pattern || !s) return(False); + + ls = strlen(s); + lp = strlen(pattern); + li = strlen(insert); + + if (!*pattern) return(False); + + while (lp <= ls && (p = strstr(s,pattern))) + { + ret = True; + memmove(p+li,p+lp,ls + 1 - (PTR_DIFF(p,s) + lp)); + memcpy(p,insert,li); + s = p + li; + ls = strlen(s); + } + return(ret); +} + + + +/********************************************************* +* Recursive routine that is called by mask_match. +* Does the actual matching. +*********************************************************/ +BOOL do_match(char *str, char *regexp, int case_sig) +{ + char *p; + + for( p = regexp; *p && *str; ) { + switch(*p) { + case '?': + str++; p++; + break; + + case '*': + /* Look for a character matching + the one after the '*' */ + p++; + if(!*p) + return True; /* Automatic match */ + while(*str) { + while(*str && (case_sig ? (*p != *str) : (toupper(*p)!=toupper(*str)))) + str++; + if(do_match(str,p,case_sig)) + return True; + if(!*str) + return False; + else + str++; + } + return False; + + default: + if(case_sig) { + if(*str != *p) + return False; + } else { + if(toupper(*str) != toupper(*p)) + return False; + } + str++, p++; + break; + } + } + if(!*p && !*str) + return True; + + if (!*p && str[0] == '.' && str[1] == 0) + return(True); + + if (!*str && *p == '?') + { + while (*p == '?') p++; + return(!*p); + } + + if(!*str && (*p == '*' && p[1] == '\0')) + return True; + return False; +} + + +/********************************************************* +* Routine to match a given string with a regexp - uses +* simplified regexp that takes * and ? only. Case can be +* significant or not. +*********************************************************/ +BOOL mask_match(char *str, char *regexp, int case_sig,BOOL trans2) +{ + char *p; + pstring p1, p2; + fstring ebase,eext,sbase,sext; + + BOOL matched; + + /* Make local copies of str and regexp */ + StrnCpy(p1,regexp,sizeof(pstring)-1); + StrnCpy(p2,str,sizeof(pstring)-1); + + if (!strchr(p2,'.')) { + strcat(p2,"."); + } + +/* + if (!strchr(p1,'.')) { + strcat(p1,"."); + } +*/ + +#if 0 + if (strchr(p1,'.')) + { + string_sub(p1,"*.*","*"); + string_sub(p1,".*","*"); + } +#endif + + /* Remove any *? and ** as they are meaningless */ + for(p = p1; *p; p++) + while( *p == '*' && (p[1] == '?' ||p[1] == '*')) + (void)strcpy( &p[1], &p[2]); + + if (strequal(p1,"*")) return(True); + + DEBUG(5,("mask_match str=<%s> regexp=<%s>, case_sig = %d\n", p2, p1, case_sig)); + + if (trans2) { + strcpy(ebase,p1); + strcpy(sbase,p2); + } else { + if ((p=strrchr(p1,'.'))) { + *p = 0; + strcpy(ebase,p1); + strcpy(eext,p+1); + } else { + strcpy(ebase,p1); + eext[0] = 0; + } + + if (!strequal(p2,".") && !strequal(p2,"..") && (p=strrchr(p2,'.'))) { + *p = 0; + strcpy(sbase,p2); + strcpy(sext,p+1); + } else { + strcpy(sbase,p2); + strcpy(sext,""); + } + } + + matched = do_match(sbase,ebase,case_sig) && + (trans2 || do_match(sext,eext,case_sig)); + + DEBUG(5,("mask_match returning %d\n", matched)); + + return matched; +} + + + +/**************************************************************************** +become a daemon, discarding the controlling terminal +****************************************************************************/ +void become_daemon(void) +{ +#ifndef NO_FORK_DEBUG + if (fork()) + exit(0); + + /* detach from the terminal */ +#ifdef USE_SETSID + setsid(); +#else +#ifdef TIOCNOTTY + { + int i = open("/dev/tty", O_RDWR); + if (i >= 0) + { + ioctl(i, (int) TIOCNOTTY, (char *)0); + close(i); + } + } +#endif +#endif +#endif +} + +/**************************************************************************** +calculate the default netmask for an address +****************************************************************************/ +static void default_netmask(struct in_addr *inm, struct in_addr *iad) +{ + unsigned long ad = ntohl(iad->s_addr); + unsigned long nm; + /* + ** Guess a netmask based on the class of the IP address given. + */ + if ( (ad & 0x80000000) == 0 ) { + /* class A address */ + nm = 0xFF000000; + } else if ( (ad & 0xC0000000) == 0x80000000 ) { + /* class B address */ + nm = 0xFFFF0000; + } else if ( (ad & 0xE0000000) == 0xC0000000 ) { + /* class C address */ + nm = 0xFFFFFF00; + } else { + /* class D or E; netmask doesn't make much sense - guess 4 bits */ + nm = 0xFFFFFFF0; + } + inm->s_addr = htonl(nm); +} + +/**************************************************************************** + get the broadcast address for our address +(troyer@saifr00.ateng.az.honeywell.com) +****************************************************************************/ +void get_broadcast(struct in_addr *if_ipaddr, + struct in_addr *if_bcast, + struct in_addr *if_nmask) +{ + BOOL found = False; +#ifndef NO_GET_BROADCAST + int sock = -1; /* AF_INET raw socket desc */ + char buff[1024]; + struct ifreq *ifr=NULL; + int i; + +#if defined(EVEREST) + int n_interfaces; + struct ifconf ifc; + struct ifreq *ifreqs; +#elif defined(USE_IFREQ) + struct ifreq ifreq; + struct strioctl strioctl; + struct ifconf *ifc; +#else + struct ifconf ifc; +#endif +#endif + + /* get a default netmask and broadcast */ + default_netmask(if_nmask, if_ipaddr); + +#ifndef NO_GET_BROADCAST + /* Create a socket to the INET kernel. */ +#if USE_SOCKRAW + if ((sock = socket(AF_INET, SOCK_RAW, PF_INET )) < 0) +#else + if ((sock = socket(AF_INET, SOCK_DGRAM, 0 )) < 0) +#endif + { + DEBUG(0,( "Unable to open socket to get broadcast address\n")); + return; + } + + /* Get a list of the configured interfaces */ +#ifdef EVEREST + /* This is part of SCO Openserver 5: The ioctls are no longer part + if the lower level STREAMS interface glue. They are now real + ioctl calls */ + + if (ioctl(sock, SIOCGIFANUM, &n_interfaces) < 0) { + DEBUG(0,( "SIOCGIFANUM: %s\n", strerror(errno))); + } else { + DEBUG(0,( "number of interfaces returned is: %d\n", n_interfaces)); + + ifc.ifc_len = sizeof(struct ifreq) * n_interfaces; + ifc.ifc_buf = (caddr_t) alloca(ifc.ifc_len); + + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) + DEBUG(0, ( "SIOCGIFCONF: %s\n", strerror(errno))); + else { + ifr = ifc.ifc_req; + + for (i = 0; i < n_interfaces; ++i) { + if (if_ipaddr->s_addr == + ((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr.s_addr) { + found = True; + break; + } + } + } + } +#elif defined(USE_IFREQ) + ifc = (struct ifconf *)buff; + ifc->ifc_len = BUFSIZ - sizeof(struct ifconf); + strioctl.ic_cmd = SIOCGIFCONF; + strioctl.ic_dp = (char *)ifc; + strioctl.ic_len = sizeof(buff); + if (ioctl(sock, I_STR, &strioctl) < 0) { + DEBUG(0,( "I_STR/SIOCGIFCONF: %s\n", strerror(errno))); + } else { + ifr = (struct ifreq *)ifc->ifc_req; + + /* Loop through interfaces, looking for given IP address */ + for (i = ifc->ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { + if (if_ipaddr->s_addr == + (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { + found = True; + break; + } + } + } +#elif defined(__FreeBSD__) || defined(NETBSD) + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno))); + } else { + ifr = ifc.ifc_req; + /* Loop through interfaces, looking for given IP address */ + i = ifc.ifc_len; + while (i > 0) { + if (if_ipaddr->s_addr == + (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { + found = True; + break; + } + i -= ifr->ifr_addr.sa_len + IFNAMSIZ; + ifr = (struct ifreq*) ((char*) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ); + } + } +#else + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { + DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno))); + } else { + ifr = ifc.ifc_req; + + /* Loop through interfaces, looking for given IP address */ + for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { +#ifdef BSDI + if (ioctl(sock, SIOCGIFADDR, ifr) < 0) break; +#endif + if (if_ipaddr->s_addr == + (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { + found = True; + break; + } + } + } +#endif + + if (!found) { + DEBUG(0,("No interface found for address %s\n", inet_ntoa(*if_ipaddr))); + } else { + /* Get the netmask address from the kernel */ +#ifdef USE_IFREQ + ifreq = *ifr; + + strioctl.ic_cmd = SIOCGIFNETMASK; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(sock, I_STR, &strioctl) < 0) + DEBUG(0,("Failed I_STR/SIOCGIFNETMASK: %s\n", strerror(errno))); + else + *if_nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; +#else + if (ioctl(sock, SIOCGIFNETMASK, ifr) < 0) + DEBUG(0,("SIOCGIFNETMASK failed\n")); + else + *if_nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; +#endif + + DEBUG(2,("Netmask for %s = %s\n", ifr->ifr_name, + inet_ntoa(*if_nmask))); + } + + /* Close up shop */ + (void) close(sock); + +#endif + + /* sanity check on the netmask */ + { + unsigned long nm = ntohl(if_nmask->s_addr); + if ((nm >> 24) != 0xFF) { + DEBUG(0,("Impossible netmask %s - using defaults\n",inet_ntoa(*if_nmask))); + default_netmask(if_nmask, if_ipaddr); + } + } + + /* derive the broadcast assuming a 1's broadcast, as this is what + all MS operating systems do, we have to comply even if the unix + box is setup differently */ + { + unsigned long ad = ntohl(if_ipaddr->s_addr); + unsigned long nm = ntohl(if_nmask->s_addr); + unsigned long bc = (ad & nm) | (0xffffffff & ~nm); + if_bcast->s_addr = htonl(bc); + } + + DEBUG(2,("Derived broadcast address %s\n", inet_ntoa(*if_bcast))); +} /* get_broadcast */ + + +/**************************************************************************** +put up a yes/no prompt +****************************************************************************/ +BOOL yesno(char *p) +{ + pstring ans; + printf("%s",p); + + if (!fgets(ans,sizeof(ans)-1,stdin)) + return(False); + + if (*ans == 'y' || *ans == 'Y') + return(True); + + return(False); +} + +/**************************************************************************** +read a line from a file with possible \ continuation chars. +Blanks at the start or end of a line are stripped. +The string will be allocated if s2 is NULL +****************************************************************************/ +char *fgets_slash(char *s2,int maxlen,FILE *f) +{ + char *s=s2; + int len = 0; + int c; + BOOL start_of_line = True; + + if (feof(f)) + return(NULL); + + if (!s2) + { + maxlen = MIN(maxlen,8); + s = (char *)Realloc(s,maxlen); + } + + if (!s || maxlen < 2) return(NULL); + + *s = 0; + + while (len < maxlen-1) + { + c = getc(f); + switch (c) + { + case '\r': + break; + case '\n': + while (len > 0 && s[len-1] == ' ') + { + s[--len] = 0; + } + if (len > 0 && s[len-1] == '\\') + { + s[--len] = 0; + start_of_line = True; + break; + } + return(s); + case EOF: + if (len <= 0 && !s2) + free(s); + return(len>0?s:NULL); + case ' ': + if (start_of_line) + break; + default: + start_of_line = False; + s[len++] = c; + s[len] = 0; + } + if (!s2 && len > maxlen-3) + { + maxlen *= 2; + s = (char *)Realloc(s,maxlen); + if (!s) return(NULL); + } + } + return(s); +} + + + +/**************************************************************************** +set the length of a file from a filedescriptor. +Returns 0 on success, -1 on failure. +****************************************************************************/ +int set_filelen(int fd, long len) +{ +/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot + extend a file with ftruncate. Provide alternate implementation + for this */ + +#if FTRUNCATE_CAN_EXTEND + return ftruncate(fd, len); +#else + struct stat st; + char c = 0; + long currpos = lseek(fd, 0L, SEEK_CUR); + + if(currpos < 0) + return -1; + /* Do an fstat to see if the file is longer than + the requested size (call ftruncate), + or shorter, in which case seek to len - 1 and write 1 + byte of zero */ + if(fstat(fd, &st)<0) + return -1; + +#ifdef S_ISFIFO + if (S_ISFIFO(st.st_mode)) return 0; +#endif + + if(st.st_size == len) + return 0; + if(st.st_size > len) + return ftruncate(fd, len); + + if(lseek(fd, len-1, SEEK_SET) != len -1) + return -1; + if(write(fd, &c, 1)!=1) + return -1; + /* Seek to where we were */ + lseek(fd, currpos, SEEK_SET); + return 0; +#endif +} + + +/**************************************************************************** +return the byte checksum of some data +****************************************************************************/ +int byte_checksum(char *buf,int len) +{ + unsigned char *p = (unsigned char *)buf; + int ret = 0; + while (len--) + ret += *p++; + return(ret); +} + + + +#ifdef HPUX +/**************************************************************************** +this is a version of setbuffer() for those machines that only have setvbuf +****************************************************************************/ +void setbuffer(FILE *f,char *buf,int bufsize) +{ + setvbuf(f,buf,_IOFBF,bufsize); +} +#endif + + +/**************************************************************************** +parse out a directory name from a path name. Assumes dos style filenames. +****************************************************************************/ +char *dirname_dos(char *path,char *buf) +{ + char *p = strrchr(path,'\\'); + + if (!p) + strcpy(buf,path); + else + { + *p = 0; + strcpy(buf,path); + *p = '\\'; + } + + return(buf); +} + + +/**************************************************************************** +parse out a filename from a path name. Assumes dos style filenames. +****************************************************************************/ +static char *filename_dos(char *path,char *buf) +{ + char *p = strrchr(path,'\\'); + + if (!p) + strcpy(buf,path); + else + strcpy(buf,p+1); + + return(buf); +} + + + +/**************************************************************************** +expand a pointer to be a particular size +****************************************************************************/ +void *Realloc(void *p,int size) +{ + void *ret=NULL; + if (!p) + ret = (void *)malloc(size); + else + ret = (void *)realloc(p,size); + + if (!ret) + DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",size)); + + return(ret); +} + +/**************************************************************************** +set the time on a file +****************************************************************************/ +BOOL set_filetime(char *fname,time_t mtime) +{ + struct utimbuf times; + + if (null_mtime(mtime)) return(True); + + times.modtime = times.actime = mtime; + + if (sys_utime(fname,×)) { + DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); + } + + return(True); +} + + +#ifdef NOSTRDUP +/**************************************************************************** +duplicate a string +****************************************************************************/ +char *strdup(char *s) +{ + char *ret = NULL; + if (!s) return(NULL); + ret = (char *)malloc(strlen(s)+1); + if (!ret) return(NULL); + strcpy(ret,s); + return(ret); +} +#endif + + +/**************************************************************************** + Signal handler for SIGPIPE (write on a disconnected socket) +****************************************************************************/ +void Abort(void ) +{ + DEBUG(0,("Probably got SIGPIPE\nExiting\n")); + exit(2); +} + + +#ifdef REPLACE_STRLEN +/**************************************************************************** +a replacement strlen() that returns int for solaris +****************************************************************************/ +int Strlen(char *s) +{ + int ret=0; + if (!s) return(0); + while (*s++) ret++; + return(ret); +} +#endif + + +/**************************************************************************** +return a time at the start of the current month +****************************************************************************/ +time_t start_of_month(void) +{ + time_t t = time(NULL); + struct tm *t2; + + t2 = gmtime(&t); + + t2->tm_mday = 1; + t2->tm_hour = 0; + t2->tm_min = 0; + t2->tm_sec = 0; + + return(mktime(t2)); +} + + +/******************************************************************* + check for a sane unix date +********************************************************************/ +BOOL sane_unix_date(time_t unixdate) +{ + struct tm t,today; + time_t t_today = time(NULL); + + t = *(LocalTime(&unixdate,LOCAL_TO_GMT)); + today = *(LocalTime(&t_today,LOCAL_TO_GMT)); + + if (t.tm_year < 80) + return(False); + + if (t.tm_year > today.tm_year) + return(False); + + if (t.tm_year == today.tm_year && + t.tm_mon > today.tm_mon) + return(False); + + + if (t.tm_year == today.tm_year && + t.tm_mon == today.tm_mon && + t.tm_mday > (today.tm_mday+1)) + return(False); + + return(True); +} + + + +#ifdef NO_FTRUNCATE + /******************************************************************* +ftruncate for operating systems that don't have it +********************************************************************/ +int ftruncate(int f,long l) +{ + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = l; + fl.l_type = F_WRLCK; + return fcntl(f, F_FREESP, &fl); +} +#endif + + + +/**************************************************************************** +get my own name and IP +****************************************************************************/ +BOOL get_myname(char *myname,struct in_addr *ip) +{ + struct hostent *hp; + pstring hostname; + + *hostname = 0; + + /* get my host name */ + if (gethostname(hostname, MAXHOSTNAMELEN) == -1) + { + DEBUG(0,("gethostname failed\n")); + return False; + } + + /* get host info */ + if ((hp = Get_Hostbyname(hostname)) == 0) + { + DEBUG(0,( "Get_Hostbyname: Unknown host %s.\n",hostname)); + return False; + } + + if (myname) + { + /* split off any parts after an initial . */ + char *p = strchr(hostname,'.'); + if (p) *p = 0; + + strcpy(myname,hostname); + } + + if (ip) + putip((char *)ip,(char *)hp->h_addr); + + return(True); +} + + +/**************************************************************************** +true if two IP addresses are equal +****************************************************************************/ +BOOL ip_equal(struct in_addr ip1,struct in_addr ip2) +{ + unsigned long a1,a2; + a1 = ntohl(ip1.s_addr); + a2 = ntohl(ip2.s_addr); + return(a1 == a2); +} + + +/**************************************************************************** +open a socket of the specified type, port and address for incoming data +****************************************************************************/ +int open_socket_in(int type, int port, int dlevel) +{ + struct hostent *hp; + struct sockaddr_in sock; + pstring host_name; + int res; + + /* get my host name */ +#ifdef MAXHOSTNAMELEN + if (gethostname(host_name, MAXHOSTNAMELEN) == -1) +#else + if (gethostname(host_name, sizeof(host_name)) == -1) +#endif + { DEBUG(0,("gethostname failed\n")); return -1; } + + /* get host info */ + if ((hp = Get_Hostbyname(host_name)) == 0) + { + DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",host_name)); + return -1; + } + + bzero((char *)&sock,sizeof(sock)); + memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length); +#if defined(__FreeBSD__) || defined(NETBSD) /* XXX not the right ifdef */ + sock.sin_len = sizeof(sock); +#endif + sock.sin_port = htons( port ); + sock.sin_family = hp->h_addrtype; + sock.sin_addr.s_addr = INADDR_ANY; + res = socket(hp->h_addrtype, type, 0); + if (res == -1) + { DEBUG(0,("socket failed\n")); return -1; } + + { + int one=1; + setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); + } + + /* now we've got a socket - we need to bind it */ + if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) + { + if (port) { + if (port == 139 || port == 137) + DEBUG(dlevel,("bind failed on port %d (%s)\n", + port,strerror(errno))); + close(res); + + if (dlevel > 0 && port < 1000) + port = 7999; + + if (port >= 1000 && port < 9000) + return(open_socket_in(type,port+1,dlevel)); + } + + return(-1); + } + DEBUG(3,("bind succeeded on port %d\n",port)); + + return res; +} + + +/**************************************************************************** + create an outgoing socket + **************************************************************************/ +int open_socket_out(int type, struct in_addr *addr, int port ) +{ + struct sockaddr_in sock_out; + int res; + + /* create a socket to write to */ + res = socket(PF_INET, type, 0); + if (res == -1) + { DEBUG(0,("socket error\n")); return -1; } + + if (type != SOCK_STREAM) return(res); + + bzero((char *)&sock_out,sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)addr); + + sock_out.sin_port = htons( port ); + sock_out.sin_family = PF_INET; + + DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port)); + + /* and connect it to the destination */ + if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))<0) { + DEBUG(0,("connect error: %s\n",strerror(errno))); + close(res); + return(-1); + } + + return res; +} + + +/**************************************************************************** +interpret a protocol description string, with a default +****************************************************************************/ +int interpret_protocol(char *str,int def) +{ + if (strequal(str,"NT1")) + return(PROTOCOL_NT1); + if (strequal(str,"LANMAN2")) + return(PROTOCOL_LANMAN2); + if (strequal(str,"LANMAN1")) + return(PROTOCOL_LANMAN1); + if (strequal(str,"CORE")) + return(PROTOCOL_CORE); + if (strequal(str,"COREPLUS")) + return(PROTOCOL_COREPLUS); + if (strequal(str,"CORE+")) + return(PROTOCOL_COREPLUS); + + DEBUG(0,("Unrecognised protocol level %s\n",str)); + + return(def); +} + +/**************************************************************************** +interpret a security level +****************************************************************************/ +int interpret_security(char *str,int def) +{ + if (strequal(str,"SERVER")) + return(SEC_SERVER); + if (strequal(str,"USER")) + return(SEC_USER); + if (strequal(str,"SHARE")) + return(SEC_SHARE); + + DEBUG(0,("Unrecognised security level %s\n",str)); + + return(def); +} + + +/**************************************************************************** +interpret an internet address or name into an IP address in 4 byte form +****************************************************************************/ +unsigned long interpret_addr(char *str) +{ + struct hostent *hp; + unsigned long res; + + if (strcmp(str,"0.0.0.0") == 0) return(0); + if (strcmp(str,"255.255.255.255") == 0) return(0xFFFFFFFF); + + /* if it's in the form of an IP address then get the lib to interpret it */ + if (isdigit(str[0])) { + res = inet_addr(str); + } else { + /* otherwise assume it's a network name of some sort and use Get_Hostbyname */ + if ((hp = Get_Hostbyname(str)) == 0) { + DEBUG(3,("Get_Hostbyname: Unknown host. %s\n",str)); + return 0; + } + putip((char *)&res,(char *)hp->h_addr); + } + + if (res == (unsigned long)-1) return(0); + + return(res); +} + +/******************************************************************* + a convenient addition to interpret_addr() + ******************************************************************/ +struct in_addr *interpret_addr2(char *str) +{ + static struct in_addr ret; + unsigned long a = interpret_addr(str); + putip((char *)&ret,(char *)&a); + return(&ret); +} + +/******************************************************************* + check if an IP is the 0.0.0.0 + ******************************************************************/ +BOOL zero_ip(struct in_addr ip) +{ + unsigned long a; + putip((char *)&a,(char *)&ip); + return(a == 0); +} + +#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) + +/**************************************************************************** +interpret an 8 byte "filetime" structure to a time_t +It's originally in "100ns units since jan 1st 1601" + +It appears to be kludge-GMT (at least for file listings). This means +its the GMT you get by taking a localtime and adding the +serverzone. This is NOT the same as GMT in some cases. This routine +converts this to real GMT. +****************************************************************************/ +time_t interpret_long_date(char *p) +{ + double d; + time_t ret; + uint32 tlow,thigh; + tlow = IVAL(p,0); + thigh = IVAL(p,4); + + if (thigh == 0) return(0); + + d = ((double)thigh)*4.0*(double)(1<<30); + d += (tlow&0xFFF00000); + d *= 1.0e-7; + + /* now adjust by 369 years to make the secs since 1970 */ + d -= TIME_FIXUP_CONSTANT; + + if (d>=MAXINT) + return(0); + + ret = (time_t)(d+0.5); + + /* this takes us from kludge-GMT to real GMT */ + ret += TimeDiff(ret) - serverzone; + + return(ret); +} + + +/**************************************************************************** +put a 8 byte filetime from a time_t +This takes real GMT as input and converts to kludge-GMT +****************************************************************************/ +void put_long_date(char *p,time_t t) +{ + uint32 tlow,thigh; + double d; + + if (t==0) { + SIVAL(p,0,0); SIVAL(p,4,0); + return; + } + + /* this converts GMT to kludge-GMT */ + t -= TimeDiff(t) - serverzone; + + d = (double) (t); + + d += TIME_FIXUP_CONSTANT; + + d *= 1.0e7; + + thigh = (uint32)(d * (1.0/(4.0*(double)(1<<30)))); + tlow = (uint32)(d - ((double)thigh)*4.0*(double)(1<<30)); + + SIVAL(p,0,tlow); + SIVAL(p,4,thigh); +} + +/******************************************************************* +sub strings with useful parameters +********************************************************************/ +void standard_sub_basic(char *s) +{ + if (!strchr(s,'%')) return; + + string_sub(s,"%R",remote_proto); + string_sub(s,"%a",remote_arch); + string_sub(s,"%m",remote_machine); + string_sub(s,"%L",local_machine); + + if (!strchr(s,'%')) return; + + string_sub(s,"%v",VERSION); + string_sub(s,"%h",myhostname); + string_sub(s,"%U",sesssetup_user); + + if (!strchr(s,'%')) return; + + string_sub(s,"%I",Client_info.addr); + string_sub(s,"%M",Client_info.name); + string_sub(s,"%T",timestring()); + + if (!strchr(s,'%')) return; + + { + char pidstr[10]; + sprintf(pidstr,"%d",(int)getpid()); + string_sub(s,"%d",pidstr); + } + + if (!strchr(s,'%')) return; + + { + struct passwd *pass = Get_Pwnam(sesssetup_user,False); + if (pass) { + string_sub(s,"%G",gidtoname(pass->pw_gid)); + } + } +} + + +/******************************************************************* +write a string in unicoode format +********************************************************************/ +int PutUniCode(char *dst,char *src) +{ + int ret = 0; + while (*src) { + dst[ret++] = src[0]; + dst[ret++] = 0; + src++; + } + dst[ret++]=0; + dst[ret++]=0; + return(ret); +} + + +pstring smbrun_path = SMBRUN; + +/**************************************************************************** +run a command via system() using smbrun +****************************************************************************/ +int smbrun(char *cmd,char *outfile) +{ + int ret; + pstring syscmd; + + if (!file_exist(smbrun_path,NULL)) + { + DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",smbrun_path)); + return(1); + } + + sprintf(syscmd,"%s \"(%s 2>&1) > %s\"", + smbrun_path,cmd, + outfile?outfile:"/dev/null"); + + DEBUG(5,("smbrun - running %s ",syscmd)); + ret = system(syscmd); + DEBUG(5,("gave %d\n",ret)); + return(ret); +} + + +/**************************************************************************** +a wrapper for gethostbyname() that tries with all lower and all upper case +if the initial name fails +****************************************************************************/ +struct hostent *Get_Hostbyname(char *name) +{ + char *name2 = strdup(name); + struct hostent *ret; + + if (!name2) + { + DEBUG(0,("Memory allocation error in Get_Hostbyname! panic\n")); + exit(0); + } + + if (!isalnum(*name2)) + { + free(name2); + return(NULL); + } + + ret = gethostbyname(name2); + if (ret != NULL) + { + free(name2); + return(ret); + } + + /* try with all lowercase */ + strlower(name2); + ret = gethostbyname(name2); + if (ret != NULL) + { + free(name2); + return(ret); + } + + /* try with all uppercase */ + strupper(name2); + ret = gethostbyname(name2); + if (ret != NULL) + { + free(name2); + return(ret); + } + + /* nothing works :-( */ + free(name2); + return(NULL); +} + + +/**************************************************************************** +check if a process exists. Does this work on all unixes? +****************************************************************************/ +BOOL process_exists(int pid) +{ +#ifdef LINUX + fstring s; + sprintf(s,"/proc/%d",pid); + return(directory_exist(s,NULL)); +#else + { + static BOOL tested=False; + static BOOL ok=False; + fstring s; + if (!tested) { + tested = True; + sprintf(s,"/proc/%05d",getpid()); + ok = file_exist(s,NULL); + } + if (ok) { + sprintf(s,"/proc/%05d",pid); + return(file_exist(s,NULL)); + } + } + + /* a best guess for non root access */ + if (geteuid() != 0) return(True); + + /* otherwise use kill */ + return(pid == getpid() || kill(pid,0) == 0); +#endif +} + + +/******************************************************************* +turn a uid into a user name +********************************************************************/ +char *uidtoname(int uid) +{ + static char name[40]; + struct passwd *pass = getpwuid(uid); + if (pass) return(pass->pw_name); + sprintf(name,"%d",uid); + return(name); +} + +/******************************************************************* +turn a gid into a group name +********************************************************************/ +char *gidtoname(int gid) +{ + static char name[40]; + struct group *grp = getgrgid(gid); + if (grp) return(grp->gr_name); + sprintf(name,"%d",gid); + return(name); +} + +/******************************************************************* +block sigs +********************************************************************/ +void BlockSignals(BOOL block) +{ +#ifdef USE_SIGBLOCK + int block_mask = (sigmask(SIGTERM)|sigmask(SIGQUIT)|sigmask(SIGSEGV) + |sigmask(SIGCHLD)|sigmask(SIGQUIT)|sigmask(SIGBUS)| + sigmask(SIGINT)); + if (block) + sigblock(block_mask); + else + sigunblock(block_mask); +#endif +} + +#if AJT +/******************************************************************* +my own panic function - not suitable for general use +********************************************************************/ +void ajt_panic(void) +{ + pstring cmd = "/usr/bin/X11/xedit -display :0 /tmp/ERROR_FAULT &"; + smbrun(cmd,NULL); +} +#endif + +#ifdef USE_DIRECT +#define DIRECT direct +#else +#define DIRECT dirent +#endif + +/******************************************************************* +a readdir wrapper which just returns the file name +also return the inode number if requested +********************************************************************/ +char *readdirname(void *p) +{ + struct DIRECT *ptr; + char *dname; + + if (!p) return(NULL); + + ptr = (struct DIRECT *)readdir(p); + if (!ptr) return(NULL); + + dname = ptr->d_name; + +#ifdef KANJI + { + static pstring buf; + strcpy(buf, dname); + unix_to_dos(buf, True); + dname = buf; + } +#endif + +#ifdef NEXT2 + if (telldir(p) < 0) return(NULL); +#endif + +#ifdef SUNOS5 + /* this handles a broken compiler setup, causing a mixture + of BSD and SYSV headers and libraries */ + { + static BOOL broken_readdir = False; + if (!broken_readdir && !(*(dname)) && strequal("..",dname-2)) + { + DEBUG(0,("Your readdir() is broken. You have somehow mixed SYSV and BSD headers and libraries\n")); + broken_readdir = True; + } + if (broken_readdir) + return(dname-2); + } +#endif + + return(dname); +} + + + +#if (defined(SecureWare) && defined(SCO)) +/* This is needed due to needing the nap() function but we don't want + to include the Xenix libraries since that will break other things... + BTW: system call # 0x0c28 is the same as calling nap() */ +long nap(long milliseconds) { + return syscall(0x0c28, milliseconds); +} +#endif + +#ifdef NO_INITGROUPS +#include <sys/types.h> +#include <limits.h> +#include <grp.h> + +#ifndef NULL +#define NULL (void *)0 +#endif + +/**************************************************************************** + some systems don't have an initgroups call +****************************************************************************/ +int initgroups(char *name,gid_t id) +{ +#ifdef NO_SETGROUPS + /* yikes! no SETGROUPS or INITGROUPS? how can this work? */ + return(0); +#else + gid_t grouplst[NGROUPS_MAX]; + int i,j; + struct group *g; + char *gr; + + grouplst[0] = id; + i = 1; + while (i < NGROUPS_MAX && + ((g = (struct group *)getgrent()) != (struct group *)NULL)) + { + if (g->gr_gid == id) + continue; + j = 0; + gr = g->gr_mem[0]; + while (gr && (*gr != (char)NULL)) { + if (strcmp(name,gr) == 0) { + grouplst[i] = g->gr_gid; + i++; + gr = (char *)NULL; + break; + } + gr = g->gr_mem[++j]; + } + } + endgrent(); + return(setgroups(i,grouplst)); +#endif +} +#endif + + +#if WRAP_MALLOC + +/* undo the wrapping temporarily */ +#undef malloc +#undef realloc +#undef free + +/**************************************************************************** +wrapper for malloc() to catch memory errors +****************************************************************************/ +void *malloc_wrapped(int size,char *file,int line) +{ +#ifdef xx_old_malloc + void *res = xx_old_malloc(size); +#else + void *res = malloc(size); +#endif + DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n", + file,line, + size,(unsigned int)res)); + return(res); +} + +/**************************************************************************** +wrapper for realloc() to catch memory errors +****************************************************************************/ +void *realloc_wrapped(void *ptr,int size,char *file,int line) +{ +#ifdef xx_old_realloc + void *res = xx_old_realloc(ptr,size); +#else + void *res = realloc(ptr,size); +#endif + DEBUG(3,("Realloc\n")); + DEBUG(3,("free called from %s(%d) with ptr=0x%X\n", + file,line, + (unsigned int)ptr)); + DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n", + file,line, + size,(unsigned int)res)); + return(res); +} + +/**************************************************************************** +wrapper for free() to catch memory errors +****************************************************************************/ +void free_wrapped(void *ptr,char *file,int line) +{ +#ifdef xx_old_free + xx_old_free(ptr); +#else + free(ptr); +#endif + DEBUG(3,("free called from %s(%d) with ptr=0x%X\n", + file,line,(unsigned int)ptr)); + return; +} + +/* and re-do the define for spots lower in this file */ +#define malloc(size) malloc_wrapped(size,__FILE__,__LINE__) +#define realloc(ptr,size) realloc_wrapped(ptr,size,__FILE__,__LINE__) +#define free(ptr) free_wrapped(ptr,__FILE__,__LINE__) + +#endif + +#ifdef REPLACE_STRSTR +/**************************************************************************** +Mips version of strstr doesn't seem to work correctly. +There is a #define in includes.h to redirect calls to this function. +****************************************************************************/ +char *Strstr(char *s, char *p) +{ + int len = strlen(p); + + while ( *s != '\0' ) { + if ( strncmp(s, p, len) == 0 ) + return s; + s++; + } + + return NULL; +} +#endif /* REPLACE_STRSTR */ + + +#ifdef REPLACE_MKTIME +/******************************************************************* +a mktime() replacement for those who don't have it - contributed by +C.A. Lademann <cal@zls.com> +********************************************************************/ +#define MINUTE 60 +#define HOUR 60*MINUTE +#define DAY 24*HOUR +#define YEAR 365*DAY +time_t Mktime(struct tm *t) +{ + struct tm *u; + time_t epoch = 0; + int mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + y, m, i; + + if(t->tm_year < 70) + return((time_t)-1); + + epoch = (t->tm_year - 70) * YEAR + + (t->tm_year / 4 - 70 / 4 - t->tm_year / 100) * DAY; + + y = t->tm_year; + m = 0; + + for(i = 0; i < t->tm_mon; i++) { + epoch += mon [m] * DAY; + if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) + epoch += DAY; + + if(++m > 11) { + m = 0; + y++; + } + } + + epoch += (t->tm_mday - 1) * DAY; + epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec; + + if((u = localtime(&epoch)) != NULL) { + t->tm_sec = u->tm_sec; + t->tm_min = u->tm_min; + t->tm_hour = u->tm_hour; + t->tm_mday = u->tm_mday; + t->tm_mon = u->tm_mon; + t->tm_year = u->tm_year; + t->tm_wday = u->tm_wday; + t->tm_yday = u->tm_yday; + t->tm_isdst = u->tm_isdst; +#ifndef NO_TM_NAME + memcpy(t->tm_name, u->tm_name, LTZNMAX); +#endif + } + + return(epoch); +} +#endif /* REPLACE_MKTIME */ + + + +#ifdef REPLACE_RENAME +/* Rename a file. (from libiberty in GNU binutils) */ +int +rename (zfrom, zto) + const char *zfrom; + const char *zto; +{ + if (link (zfrom, zto) < 0) + { + if (errno != EEXIST) + return -1; + if (unlink (zto) < 0 + || link (zfrom, zto) < 0) + return -1; + } + return unlink (zfrom); +} +#endif + + +#ifdef REPLACE_INNETGR +/* + * Search for a match in a netgroup. This replaces it on broken systems. + */ +int InNetGr(group, host, user, dom) + char *group, *host, *user, *dom; +{ + char *hst, *usr, *dm; + + setnetgrent(group); + while (getnetgrent(&hst, &usr, &dm)) + if (((host == 0) || (hst == 0) || !strcmp(host, hst)) && + ((user == 0) || (usr == 0) || !strcmp(user, usr)) && + ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) { + endnetgrent(); + return (1); + } + endnetgrent(); + return (0); +} +#endif + + +#if WRAP_MEMCPY +#undef memcpy +/******************************************************************* +a wrapper around memcpy for diagnostic purposes +********************************************************************/ +void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line) +{ + if (l>64 && (((int)d)%4) != (((int)s)%4)) + DEBUG(4,("Misaligned memcpy(0x%X,0x%X,%d) at %s(%d)\n",d,s,l,fname,line)); +#ifdef xx_old_memcpy + return(xx_old_memcpy(d,s,l)); +#else + return(memcpy(d,s,l)); +#endif +} +#define memcpy(d,s,l) memcpy_wrapped(d,s,l,__FILE__,__LINE__) +#endif + + + diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c new file mode 100644 index 0000000000..6743227173 --- /dev/null +++ b/source3/libsmb/nmblib.c @@ -0,0 +1,936 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1995 + + 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. + +*/ + +#include "includes.h" +#include "nameserv.h" + +extern int DEBUGLEVEL; + +int num_good_sends=0; +int num_good_receives=0; +static uint16 name_trn_id = 0; +BOOL CanRecurse = True; +extern pstring scope; + +/******************************************************************* + handle "compressed" name pointers + ******************************************************************/ +static BOOL handle_name_ptrs(unsigned char *ubuf,int *offset,int length, + BOOL *got_pointer,int *ret) +{ + int loop_count=0; + + while ((ubuf[*offset] & 0xC0) == 0xC0) { + if (!*got_pointer) (*ret) += 2; + (*got_pointer)=True; + (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1]; + if (loop_count++ == 10 || (*offset) < 0 || (*offset)>(length-2)) { + return(False); + } + } + return(True); +} + +/******************************************************************* + parse a nmb name from "compressed" format to something readable + return the space taken by the name, or 0 if the name is invalid + ******************************************************************/ +static int parse_nmb_name(char *inbuf,int offset,int length, + struct nmb_name *name) +{ + int m,n=0; + unsigned char *ubuf = (unsigned char *)inbuf; + int ret = 0; + BOOL got_pointer=False; + + if (length - offset < 2) return(0); + + /* handle initial name pointers */ + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) return(0); + + m = ubuf[offset]; + + if (!m) return(0); + if ((m & 0xC0) || offset+m+2 > length) return(0); + + bzero((char *)name,sizeof(*name)); + + /* the "compressed" part */ + if (!got_pointer) ret += m + 2; + offset++; + while (m) { + unsigned char c1,c2; + c1 = ubuf[offset++]-'A'; + c2 = ubuf[offset++]-'A'; + if ((c1 & 0xF0) || (c2 & 0xF0)) return(0); + name->name[n++] = (c1<<4) | c2; + m -= 2; + } + name->name[n] = 0; + + if (n==16) { + /* parse out the name type, + its always in the 16th byte of the name */ + name->name_type = name->name[15]; + + /* remove trailing spaces */ + name->name[15] = 0; + n = 14; + while (n && name->name[n]==' ') name->name[n--] = 0; + } + + /* now the domain parts (if any) */ + n = 0; + while ((m=ubuf[offset])) { + /* we can have pointers within the domain part as well */ + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) return(0); + + if (!got_pointer) ret += m+1; + if (n) name->scope[n++] = '.'; + if (m+2+offset>length || n+m+1>sizeof(name->scope)) return(0); + offset++; + while (m--) name->scope[n++] = (char)ubuf[offset++]; + } + name->scope[n++] = 0; + + return(ret); +} + + +/******************************************************************* + put a compressed nmb name into a buffer. return the length of the + compressed name + + compressed names are really weird. The "compression" doubles the + size. The idea is that it also means that compressed names conform + to the doman name system. See RFC1002. + ******************************************************************/ +static int put_nmb_name(char *buf,int offset,struct nmb_name *name) +{ + int ret,m; + fstring buf1; + char *p; + + if (name->name[0] == '*') { + /* special case for wildcard name */ + bzero(buf1,20); + buf1[0] = '*'; + } else { + sprintf(buf1,"%-15.15s%c",name->name,name->name_type); + } + + buf[offset] = 0x20; + + ret = 34; + + for (m=0;m<16;m++) { + buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF); + buf[offset+2+2*m] = 'A' + (buf1[m]&0xF); + } + offset += 33; + + buf[offset] = 0; + + if (name->scope[0]) { + /* XXXX this scope handling needs testing */ + ret += strlen(name->scope) + 1; + strcpy(&buf[offset+1],name->scope); + + p = &buf[offset+1]; + while ((p = strchr(p,'.'))) { + buf[offset] = PTR_DIFF(p,&buf[offset]); + offset += buf[offset]; + p = &buf[offset+1]; + } + buf[offset] = strlen(&buf[offset+1]); + } + + return(ret); +} + +/******************************************************************* + useful for debugging messages + ******************************************************************/ +char *namestr(struct nmb_name *n) +{ + static int i=0; + static fstring ret[4]; + char *p = ret[i]; + + if (!n->scope[0]) + sprintf(p,"%s(%x)",n->name,n->name_type); + else + sprintf(p,"%s(%x).%s",n->name,n->name_type,n->scope); + + i = (i+1)%4; + return(p); +} + +/******************************************************************* + allocate are parse some resource records + ******************************************************************/ +static BOOL parse_alloc_res_rec(char *inbuf,int *offset,int length, + struct res_rec **recs, + int count) +{ + int i; + *recs = (struct res_rec *)malloc(sizeof(**recs)*count); + if (!*recs) return(False); + + bzero(*recs,sizeof(**recs)*count); + + for (i=0;i<count;i++) { + int l = parse_nmb_name(inbuf,*offset,length,&(*recs)[i].rr_name); + (*offset) += l; + if (!l || (*offset)+10 > length) { + free(*recs); + return(False); + } + (*recs)[i].rr_type = RSVAL(inbuf,(*offset)); + (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2); + (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4); + (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8); + (*offset) += 10; + if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) || + (*offset)+(*recs)[i].rdlength > length) { + free(*recs); + return(False); + } + memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength); + (*offset) += (*recs)[i].rdlength; + } + return(True); +} + +/******************************************************************* + put a resource record into a packet + ******************************************************************/ +static int put_res_rec(char *buf,int offset,struct res_rec *recs,int count) +{ + int ret=0; + int i; + + for (i=0;i<count;i++) { + int l = put_nmb_name(buf,offset,&recs[i].rr_name); + offset += l; + ret += l; + RSSVAL(buf,offset,recs[i].rr_type); + RSSVAL(buf,offset+2,recs[i].rr_class); + RSIVAL(buf,offset+4,recs[i].ttl); + RSSVAL(buf,offset+8,recs[i].rdlength); + memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength); + offset += 10+recs[i].rdlength; + ret += 10+recs[i].rdlength; + } + + return(ret); +} + +/******************************************************************* + parse a dgram packet. Return False if the packet can't be parsed + or is invalid for some reason, True otherwise + + this is documented in section 4.4.1 of RFC1002 + ******************************************************************/ +static BOOL parse_dgram(char *inbuf,int length,struct dgram_packet *dgram) +{ + int offset; + int flags; + + bzero((char *)dgram,sizeof(*dgram)); + + if (length < 14) return(False); + + dgram->header.msg_type = CVAL(inbuf,0); + flags = CVAL(inbuf,1); + dgram->header.flags.node_type = (enum node_type)((flags>>2)&3); + if (flags & 1) dgram->header.flags.more = True; + if (flags & 2) dgram->header.flags.first = True; + dgram->header.dgm_id = RSVAL(inbuf,2); + putip((char *)&dgram->header.source_ip,inbuf+4); + dgram->header.source_port = RSVAL(inbuf,8); + dgram->header.dgm_length = RSVAL(inbuf,10); + dgram->header.packet_offset = RSVAL(inbuf,12); + + offset = 14; + + if (dgram->header.msg_type == 0x10 || + dgram->header.msg_type == 0x11 || + dgram->header.msg_type == 0x12) { + offset += parse_nmb_name(inbuf,offset,length,&dgram->source_name); + offset += parse_nmb_name(inbuf,offset,length,&dgram->dest_name); + } + + if (offset >= length || (length-offset > sizeof(dgram->data))) + return(False); + + dgram->datasize = length-offset; + memcpy(dgram->data,inbuf+offset,dgram->datasize); + + return(True); +} + + +/******************************************************************* + parse a nmb packet. Return False if the packet can't be parsed + or is invalid for some reason, True otherwise + ******************************************************************/ +static BOOL parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) +{ + int nm_flags,offset; + + bzero((char *)nmb,sizeof(*nmb)); + + if (length < 12) return(False); + + /* parse the header */ + nmb->header.name_trn_id = RSVAL(inbuf,0); + nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF; + nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False; + nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4); + nmb->header.nm_flags.bcast = (nm_flags&1)?True:False; + nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False; + nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False; + nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False; + nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False; + nmb->header.rcode = CVAL(inbuf,3) & 0xF; + nmb->header.qdcount = RSVAL(inbuf,4); + nmb->header.ancount = RSVAL(inbuf,6); + nmb->header.nscount = RSVAL(inbuf,8); + nmb->header.arcount = RSVAL(inbuf,10); + + if (nmb->header.qdcount) { + offset = parse_nmb_name(inbuf,12,length,&nmb->question.question_name); + if (!offset) return(False); + + if (length - (12+offset) < 4) return(False); + nmb->question.question_type = RSVAL(inbuf,12+offset); + nmb->question.question_class = RSVAL(inbuf,12+offset+2); + + offset += 12+4; + } else { + offset = 12; + } + + /* and any resource records */ + if (nmb->header.ancount && + !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers, + nmb->header.ancount)) + return(False); + + if (nmb->header.nscount && + !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs, + nmb->header.nscount)) + return(False); + + if (nmb->header.arcount && + !parse_alloc_res_rec(inbuf,&offset,length,&nmb->additional, + nmb->header.arcount)) + return(False); + + return(True); +} + +/******************************************************************* + free up any resources associated with an nmb packet + ******************************************************************/ +void free_nmb_packet(struct nmb_packet *nmb) +{ + if (nmb->answers) free(nmb->answers); + if (nmb->nsrecs) free(nmb->nsrecs); + if (nmb->additional) free(nmb->additional); +} + +/******************************************************************* + free up any resources associated with a packet + ******************************************************************/ +void free_packet(struct packet_struct *packet) +{ + if (packet->packet_type == NMB_PACKET) + free_nmb_packet(&packet->packet.nmb); + free(packet); +} + +/******************************************************************* + read a packet from a socket and parse it, returning a packet ready + to be used or put on the queue. This assumes a UDP socket + ******************************************************************/ +struct packet_struct *read_packet(int fd,enum packet_type packet_type) +{ + extern struct in_addr lastip; + extern int lastport; + struct packet_struct *packet; + char buf[MAX_DGRAM_SIZE]; + int length; + BOOL ok=False; + + length = read_udp_socket(fd,buf,sizeof(buf)); + if (length < MIN_DGRAM_SIZE) return(NULL); + + packet = (struct packet_struct *)malloc(sizeof(*packet)); + if (!packet) return(NULL); + + packet->next = NULL; + packet->prev = NULL; + packet->ip = lastip; + packet->port = lastport; + packet->fd = fd; + packet->timestamp = time(NULL); + packet->packet_type = packet_type; + switch (packet_type) + { + case NMB_PACKET: + ok = parse_nmb(buf,length,&packet->packet.nmb); + break; + + case DGRAM_PACKET: + ok = parse_dgram(buf,length,&packet->packet.dgram); + break; + } + if (!ok) { + free(packet); + return(NULL); + } + + num_good_receives++; + + DEBUG(4,("%s received a packet of len %d from (%s) port %d\n", + timestring(),length,inet_ntoa(packet->ip),packet->port)); + + return(packet); +} + + +/******************************************************************* + send a udp packet on a already open socket + ******************************************************************/ +static BOOL send_udp(int fd,char *buf,int len,struct in_addr ip,int port) +{ + BOOL ret; + struct sockaddr_in sock_out; + + /* set the address and port */ + bzero((char *)&sock_out,sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)&ip); + sock_out.sin_port = htons( port ); + sock_out.sin_family = AF_INET; + + DEBUG(4,("%s sending a packet of len %d to (%s) on port %d\n", + timestring(),len,inet_ntoa(ip),port)); + + ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, + sizeof(sock_out)) >= 0); + + if (!ret) + DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n", + inet_ntoa(ip),port,strerror(errno))); + + if (ret) + num_good_sends++; + + return(ret); +} + +/******************************************************************* + build a dgram packet ready for sending + + XXXX This currently doesn't handle packets too big for one + datagram. It should split them and use the packet_offset, more and + first flags to handle the fragmentation. Yuck. + ******************************************************************/ +static int build_dgram(char *buf,struct packet_struct *p) +{ + struct dgram_packet *dgram = &p->packet.dgram; + unsigned char *ubuf = (unsigned char *)buf; + int offset=0; + + /* put in the header */ + ubuf[0] = dgram->header.msg_type; + ubuf[1] = (((int)dgram->header.flags.node_type)<<2); + if (dgram->header.flags.more) ubuf[1] |= 1; + if (dgram->header.flags.first) ubuf[1] |= 2; + RSSVAL(ubuf,2,dgram->header.dgm_id); + putip(ubuf+4,(char *)&dgram->header.source_ip); + RSSVAL(ubuf,8,dgram->header.source_port); + RSSVAL(ubuf,12,dgram->header.packet_offset); + + offset = 14; + + if (dgram->header.msg_type == 0x10 || + dgram->header.msg_type == 0x11 || + dgram->header.msg_type == 0x12) { + offset += put_nmb_name((char *)ubuf,offset,&dgram->source_name); + offset += put_nmb_name((char *)ubuf,offset,&dgram->dest_name); + } + + memcpy(ubuf+offset,dgram->data,dgram->datasize); + offset += dgram->datasize; + + /* automatically set the dgm_length */ + dgram->header.dgm_length = offset; + RSSVAL(ubuf,10,dgram->header.dgm_length); + + return(offset); +} + +/******************************************************************* + build a nmb name + ******************************************************************/ +void make_nmb_name(struct nmb_name *n,char *name,int type,char *this_scope) +{ + strcpy(n->name,name); + strupper(n->name); + n->name_type = type; + strcpy(n->scope,this_scope); +} + + +/******************************************************************* + build a nmb packet ready for sending + + XXXX this currently relies on not being passed something that expands + to a packet too big for the buffer. Eventually this should be + changed to set the trunc bit so the receiver can request the rest + via tcp (when that becomes supported) + ******************************************************************/ +static int build_nmb(char *buf,struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + unsigned char *ubuf = (unsigned char *)buf; + int offset=0; + + /* put in the header */ + RSSVAL(ubuf,offset,nmb->header.name_trn_id); + ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3; + if (nmb->header.response) ubuf[offset+2] |= (1<<7); + if (nmb->header.nm_flags.authoritative) ubuf[offset+2] |= 0x4; + if (nmb->header.nm_flags.trunc) ubuf[offset+2] |= 0x2; + if (nmb->header.nm_flags.recursion_desired) ubuf[offset+2] |= 0x1; + if (nmb->header.nm_flags.recursion_available) ubuf[offset+3] |= 0x80; + if (nmb->header.nm_flags.bcast) ubuf[offset+3] |= 0x10; + ubuf[offset+3] |= (nmb->header.rcode & 0xF); + RSSVAL(ubuf,offset+4,nmb->header.qdcount); + RSSVAL(ubuf,offset+6,nmb->header.ancount); + RSSVAL(ubuf,offset+8,nmb->header.nscount); + RSSVAL(ubuf,offset+10,nmb->header.arcount); + + offset += 12; + if (nmb->header.qdcount) { + /* XXXX this doesn't handle a qdcount of > 1 */ + offset += put_nmb_name((char *)ubuf,offset,&nmb->question.question_name); + RSSVAL(ubuf,offset,nmb->question.question_type); + RSSVAL(ubuf,offset+2,nmb->question.question_class); + offset += 4; + } + + if (nmb->header.ancount) + offset += put_res_rec((char *)ubuf,offset,nmb->answers, + nmb->header.ancount); + + if (nmb->header.nscount) + offset += put_res_rec((char *)ubuf,offset,nmb->nsrecs, + nmb->header.nscount); + + if (nmb->header.arcount) + offset += put_res_rec((char *)ubuf,offset,nmb->additional, + nmb->header.arcount); + + return(offset); +} + + +/******************************************************************* + send a packet_struct + ******************************************************************/ +BOOL send_packet(struct packet_struct *p) +{ + char buf[1024]; + int len=0; + + bzero(buf,sizeof(buf)); + + switch (p->packet_type) + { + case NMB_PACKET: + len = build_nmb(buf,p); + break; + + case DGRAM_PACKET: + len = build_dgram(buf,p); + break; + } + + if (!len) return(False); + + return(send_udp(p->fd,buf,len,p->ip,p->port)); +} + +/**************************************************************************** + receive a packet with timeout on a open UDP filedescriptor + The timeout is in milliseconds + ***************************************************************************/ +struct packet_struct *receive_packet(int fd,enum packet_type type,int t) +{ + fd_set fds; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + timeout.tv_sec = t/1000; + timeout.tv_usec = 1000*(t%1000); + + sys_select(&fds,&timeout); + + if (FD_ISSET(fd,&fds)) + return(read_packet(fd,type)); + + return(NULL); +} + + +/**************************************************************************** +interpret a node status response +****************************************************************************/ +static void interpret_node_status(char *p, char *master,char *rname) +{ + int level = (master||rname)?4:0; + int numnames = CVAL(p,0); + DEBUG(level,("received %d names\n",numnames)); + + if (rname) *rname = 0; + if (master) *master = 0; + + p += 1; + while (numnames--) + { + char qname[17]; + int type; + fstring flags; + *flags = 0; + StrnCpy(qname,p,15); + type = CVAL(p,15); + p += 16; + + if (p[0] & 0x80) strcat(flags,"<GROUP> "); + if ((p[0] & 0x60) == 0) strcat(flags,"B "); + if ((p[0] & 0x60) == 1) strcat(flags,"P "); + if ((p[0] & 0x60) == 2) strcat(flags,"M "); + if ((p[0] & 0x60) == 3) strcat(flags,"_ "); + if (p[0] & 0x10) strcat(flags,"<DEREGISTERING> "); + if (p[0] & 0x08) strcat(flags,"<CONFLICT> "); + if (p[0] & 0x04) strcat(flags,"<ACTIVE> "); + if (p[0] & 0x02) strcat(flags,"<PERMANENT> "); + + if (master && !*master && type == 0x1d) { + StrnCpy(master,qname,15); + trim_string(master,NULL," "); + } + + if (rname && !*rname && type == 0x20 && !(p[0]&0x80)) { + StrnCpy(rname,qname,15); + trim_string(rname,NULL," "); + } + + DEBUG(level,("\t%s (type=0x%x)\t%s\n",qname,type,flags)); + p+=2; + } + DEBUG(level,("num_good_sends=%d num_good_receives=%d\n", + IVAL(p,20),IVAL(p,24))); +} + + +/**************************************************************************** + do a netbios name status query on a host + + the "master" parameter is a hack used for finding workgroups. + **************************************************************************/ +BOOL name_status(int fd,char *name,int name_type,BOOL recurse, + struct in_addr to_ip,char *master,char *rname, + void (*fn)()) +{ + BOOL found=False; + int retries = 2; + int retry_time = 5000; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + + bzero((char *)&p,sizeof(p)); + + if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + + (getpid()%(unsigned)100); + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + + nmb->header.name_trn_id = name_trn_id; + nmb->header.opcode = 0; + nmb->header.response = False; + nmb->header.nm_flags.bcast = False; + nmb->header.nm_flags.recursion_available = CanRecurse; + nmb->header.nm_flags.recursion_desired = recurse; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + make_nmb_name(&nmb->question.question_name,name,name_type,scope); + + nmb->question.question_type = 0x21; + nmb->question.question_class = 0x1; + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) + return(False); + + retries--; + + while (1) + { + struct timeval tval2; + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) break; + if (!found && !send_packet(&p)) + return False; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_packet(fd,NMB_PACKET,90))) + { + struct nmb_packet *nmb2 = &p2->packet.nmb; + if (nmb->header.name_trn_id != nmb2->header.name_trn_id || + !nmb2->header.response) { + /* its not for us - maybe deal with it later */ + if (fn) + fn(p2); + else + free_packet(p2); + continue; + } + + if (nmb2->header.opcode != 0 || + nmb2->header.nm_flags.bcast || + nmb2->header.rcode || + !nmb2->header.ancount || + nmb2->answers->rr_type != 0x21) { + /* XXXX what do we do with this? could be a redirect, but + we'll discard it for the moment */ + free_packet(p2); + continue; + } + + interpret_node_status(&nmb2->answers->rdata[0], master,rname); + free_packet(p2); + return(True); + } + } + + + DEBUG(0,("No status response (this is not unusual)\n")); + + return(False); +} + + +/**************************************************************************** + do a netbios name query to find someones IP + ****************************************************************************/ +BOOL name_query(int fd,char *name,int name_type, + BOOL bcast,BOOL recurse, + struct in_addr to_ip, struct in_addr *ip,void (*fn)()) +{ + BOOL found=False; + int retries = 3; + int retry_time = bcast?250:2000; + struct timeval tval; + struct packet_struct p; + struct packet_struct *p2; + struct nmb_packet *nmb = &p.packet.nmb; + + bzero((char *)&p,sizeof(p)); + + if (!name_trn_id) name_trn_id = (time(NULL)%(unsigned)0x7FFF) + + (getpid()%(unsigned)100); + name_trn_id = (name_trn_id+1) % (unsigned)0x7FFF; + + nmb->header.name_trn_id = name_trn_id; + nmb->header.opcode = 0; + nmb->header.response = False; + nmb->header.nm_flags.bcast = bcast; + nmb->header.nm_flags.recursion_available = CanRecurse; + nmb->header.nm_flags.recursion_desired = recurse; + nmb->header.nm_flags.trunc = False; + nmb->header.nm_flags.authoritative = False; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + make_nmb_name(&nmb->question.question_name,name,name_type,scope); + + nmb->question.question_type = 0x20; + nmb->question.question_class = 0x1; + + p.ip = to_ip; + p.port = NMB_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = NMB_PACKET; + + GetTimeOfDay(&tval); + + if (!send_packet(&p)) + return(False); + + retries--; + + while (1) + { + struct timeval tval2; + GetTimeOfDay(&tval2); + if (TvalDiff(&tval,&tval2) > retry_time) { + if (!retries) break; + if (!found && !send_packet(&p)) + return False; + GetTimeOfDay(&tval); + retries--; + } + + if ((p2=receive_packet(fd,NMB_PACKET,90))) + { + struct nmb_packet *nmb2 = &p2->packet.nmb; + if (nmb->header.name_trn_id != nmb2->header.name_trn_id || + !nmb2->header.response) { + /* its not for us - maybe deal with it later + (put it on the queue?) */ + if (fn) + fn(p2); + else + free_packet(p2); + continue; + } + + if (nmb2->header.opcode != 0 || + nmb2->header.nm_flags.bcast || + nmb2->header.rcode || + !nmb2->header.ancount) { + /* XXXX what do we do with this? could be a redirect, but + we'll discard it for the moment */ + free_packet(p2); + continue; + } + + if (ip) { + putip((char *)ip,&nmb2->answers->rdata[2]); + DEBUG(fn?3:2,("Got a positive name query response from %s", + inet_ntoa(p2->ip))); + DEBUG(fn?3:2,(" (%s)\n",inet_ntoa(*ip))); + } + found=True; retries=0; + free_packet(p2); + if (fn) break; + } + } + + return(found); +} + + +/**************************************************************************** + construct and send a netbios DGRAM + + Note that this currently sends all answers to port 138. thats the + wrong things to do! I should send to the requestors port. XXX + **************************************************************************/ +BOOL send_mailslot_reply(char *mailslot,int fd,char *buf,int len, + char *srcname,char *dstname, + int src_type,int dest_type, + struct in_addr dest_ip, + struct in_addr src_ip) +{ + struct packet_struct p; + struct dgram_packet *dgram = &p.packet.dgram; + char *ptr,*p2; + char tmp[4]; + + bzero((char *)&p,sizeof(p)); + + dgram->header.msg_type = 0x11; /* DIRECT GROUP DATAGRAM */ + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = name_trn_id++; + dgram->header.source_ip = src_ip; + dgram->header.source_port = DGRAM_PORT; + dgram->header.dgm_length = 0; /* let build_dgram() handle this */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,src_type,scope); + make_nmb_name(&dgram->dest_name,dstname,dest_type,scope); + + ptr = &dgram->data[0]; + + /* now setup the smb part */ + ptr -= 4; /* XXX ugliness because of handling of tcp SMB length */ + memcpy(tmp,ptr,4); + set_message(ptr,17,17 + len,True); + memcpy(ptr,tmp,4); + + CVAL(ptr,smb_com) = SMBtrans; + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,1); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + strcpy(p2,mailslot); + p2 = skip_string(p2,1); + + memcpy(p2,buf,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length */ + + p.ip = dest_ip; + p.port = DGRAM_PORT; + p.fd = fd; + p.timestamp = time(NULL); + p.packet_type = DGRAM_PACKET; + + return(send_packet(&p)); +} + + diff --git a/source3/libsmb/smbencrypt.c b/source3/libsmb/smbencrypt.c new file mode 100644 index 0000000000..a0683b5d28 --- /dev/null +++ b/source3/libsmb/smbencrypt.c @@ -0,0 +1,202 @@ +#ifdef SMB_PASSWD +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB parameters and setup + Copyright (C) Andrew Tridgell 1992-1995 + Modified by Jeremy Allison 1995. + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +#include "des.h" +#include "md4.h" + +extern int DEBUGLEVEL; + +#ifndef uchar +#define uchar unsigned char +#endif +#ifndef int16 +#define int16 unsigned short +#endif +#ifndef uint16 +#define uint16 unsigned short +#endif +#ifndef uint32 +#define uint32 unsigned int +#endif + +#include "byteorder.h" + +void str_to_key(uchar *str,uchar *key) +{ + void des_set_odd_parity(des_cblock *); + int i; + + key[0] = str[0]>>1; + key[1] = ((str[0]&0x01)<<6) | (str[1]>>2); + key[2] = ((str[1]&0x03)<<5) | (str[2]>>3); + key[3] = ((str[2]&0x07)<<4) | (str[3]>>4); + key[4] = ((str[3]&0x0F)<<3) | (str[4]>>5); + key[5] = ((str[4]&0x1F)<<2) | (str[5]>>6); + key[6] = ((str[5]&0x3F)<<1) | (str[6]>>7); + key[7] = str[6]&0x7F; + for (i=0;i<8;i++) { + key[i] = (key[i]<<1); + } + des_set_odd_parity((des_cblock *)key); +} + +void D1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); + des_set_key(deskey,ks); + des_ecb_encrypt(d, out, ks, DES_DECRYPT); +} + +void E1(uchar *k, uchar *d, uchar *out) +{ + des_key_schedule ks; + des_cblock deskey; + + str_to_key(k,(uchar *)deskey); + des_set_key(deskey,ks); + des_ecb_encrypt(d, out, ks, DES_ENCRYPT); +} + +void E_P16(uchar *p14,uchar *p16) +{ + uchar sp7[7]; + /* the following constant makes us compatible with other + implementations. Note that publishing this constant does not reduce the + security of the encryption mechanism */ + uchar sp8[] = {0xAA,0xD3,0xB4,0x35,0xB5,0x14,0x4,0xEE}; + uchar x[8]; + + memset(sp7,'\0',7); + + D1(sp7, sp8, x); + E1(p14, x, p16); + E1(p14+7, x, p16+8); +} + +void E_P24(uchar *p21, uchar *c8, uchar *p24) +{ + E1(p21, c8, p24); + E1(p21+7, c8, p24+8); + E1(p21+14, c8, p24+16); +} + + +/* + This implements the X/Open SMB password encryption + It takes a password, a 8 byte "crypt key" and puts 24 bytes of + encrypted password into p24 */ +void SMBencrypt(uchar *passwd, uchar *c8, uchar *p24) +{ + uchar p14[15], p21[21]; + + memset(p21,'\0',21); + memset(p14,'\0',14); + StrnCpy((char *)p14,(char *)passwd,14); + + strupper((char *)p14); + E_P16(p14, p21); + E_P24(p21, c8, p24); +} + +/* Routines for Windows NT MD4 Hash functions. */ +static int _my_wcslen(int16 *str) +{ + int len = 0; + while(*str++ != 0) + len++; + return len; +} + +/* + * Convert a string into an NT UNICODE string. + * Note that regardless of processor type + * this must be in intel (little-endian) + * format. + */ + +static int _my_mbstowcs(int16 *dst, uchar *src, int len) +{ + int i; + int16 val; + + for(i = 0; i < len; i++) { + val = *src; + SSVAL(dst,0,val); + dst++; + src++; + if(val == 0) + break; + } + return i; +} + +/* + * Creates the MD4 Hash of the users password in NT UNICODE. + */ + +void E_md4hash(uchar *passwd, uchar *p16) +{ + int i, len; + int16 wpwd[129]; + MDstruct MD; + + /* Password cannot be longer than 128 characters */ + len = strlen(passwd); + if(len > 128) + len = 128; + /* Password must be converted to NT unicode */ + _my_mbstowcs( wpwd, passwd, len); + wpwd[len] = 0; /* Ensure string is null terminated */ + /* Calculate length in bytes */ + len = _my_wcslen(wpwd) * sizeof(int16); + + MDbegin(&MD); + for(i = 0; i + 64 <= len; i += 64) + MDupdate(&MD,wpwd + (i/2), 512); + MDupdate(&MD,wpwd + (i/2),(len-i)*8); + SIVAL(p16,0,MD.buffer[0]); + SIVAL(p16,4,MD.buffer[1]); + SIVAL(p16,8,MD.buffer[2]); + SIVAL(p16,12,MD.buffer[3]); +} + +/* Does the NT MD4 hash then des encryption. */ + +void SMBNTencrypt(uchar *passwd, uchar *c8, uchar *p24) +{ + uchar p21[21]; + + memset(p21,'\0',21); + + E_md4hash(passwd, p21); + E_P24(p21, c8, p24); +} + +#else +void smbencrypt_dummy(void){} +#endif diff --git a/source3/locking/locking.c b/source3/locking/locking.c new file mode 100644 index 0000000000..6ff3ab5d12 --- /dev/null +++ b/source3/locking/locking.c @@ -0,0 +1,330 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Locking functions + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +extern int DEBUGLEVEL; +extern connection_struct Connections[]; +extern files_struct Files[]; + +pstring share_del_pending=""; + + +/**************************************************************************** + utility function called to see if a file region is locked +****************************************************************************/ +BOOL is_locked(int fnum,int cnum,uint32 count,uint32 offset) +{ + int snum = SNUM(cnum); + + if (count == 0) + return(False); + + if (!lp_locking(snum) || !lp_strict_locking(snum)) + return(False); + + return(fcntl_lock(Files[fnum].fd,F_GETLK,offset,count, + (Files[fnum].can_write?F_WRLCK:F_RDLCK))); +} + + +/**************************************************************************** + utility function called by locking requests +****************************************************************************/ +BOOL do_lock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode) +{ + BOOL ok = False; + + if (!lp_locking(SNUM(cnum))) + return(True); + + if (count == 0) { + *eclass = ERRDOS; + *ecode = ERRnoaccess; + return False; + } + + if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum)) + ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count, + (Files[fnum].can_write?F_WRLCK:F_RDLCK)); + + if (!ok) { + *eclass = ERRDOS; + *ecode = ERRlock; + return False; + } + return True; /* Got lock */ +} + + +/**************************************************************************** + utility function called by unlocking requests +****************************************************************************/ +BOOL do_unlock(int fnum,int cnum,uint32 count,uint32 offset,int *eclass,uint32 *ecode) +{ + BOOL ok = False; + + if (!lp_locking(SNUM(cnum))) + return(True); + + if (Files[fnum].can_lock && OPEN_FNUM(fnum) && (Files[fnum].cnum == cnum)) + ok = fcntl_lock(Files[fnum].fd,F_SETLK,offset,count,F_UNLCK); + + if (!ok) { + *eclass = ERRDOS; + *ecode = ERRlock; + return False; + } + return True; /* Did unlock */ +} + +/******************************************************************* + name a share file + ******************************************************************/ +static BOOL share_name(int cnum,struct stat *st,char *name) +{ + strcpy(name,lp_lockdir()); + standard_sub(cnum,name); + trim_string(name,"","/"); + if (!*name) return(False); + name += strlen(name); + + sprintf(name,"/share.%d.%d",(int)st->st_dev,(int)st->st_ino); + return(True); +} + +/******************************************************************* + use the fnum to get the share file name + ******************************************************************/ +static BOOL share_name_fnum(int fnum,char *name) +{ + struct stat st; + if (fstat(Files[fnum].fd,&st) != 0) return(False); + return(share_name(Files[fnum].cnum,&st,name)); +} + + +/******************************************************************* + get the share mode of a file using the fnum + ******************************************************************/ +int get_share_mode_by_fnum(int cnum,int fnum,int *pid) +{ + struct stat sbuf; + if (fstat(Files[fnum].fd,&sbuf) == -1) return(0); + return(get_share_mode(cnum,&sbuf,pid)); +} + +/******************************************************************* + get the share mode of a file using the files name + ******************************************************************/ +int get_share_mode_byname(int cnum,char *fname,int *pid) +{ + struct stat sbuf; + if (stat(fname,&sbuf) == -1) return(0); + return(get_share_mode(cnum,&sbuf,pid)); +} + + +/******************************************************************* +get the share mode of a file +********************************************************************/ +int get_share_mode(int cnum,struct stat *sbuf,int *pid) +{ + pstring fname; + int fd2; + char buf[16]; + int ret; + time_t t; + + *pid = 0; + + if (!share_name(cnum,sbuf,fname)) return(0); + + fd2 = open(fname,O_RDONLY,0); + if (fd2 < 0) return(0); + + if (read(fd2,buf,16) != 16) { + close(fd2); + unlink(fname); + return(0); + } + close(fd2); + + t = IVAL(buf,0); + ret = IVAL(buf,4); + *pid = IVAL(buf,8); + + if (IVAL(buf,12) != LOCKING_VERSION) { + if (!unlink(fname)) DEBUG(2,("Deleted old locking file %s",fname)); + *pid = 0; + return(0); + } + + if (*pid && !process_exists(*pid)) { + ret=0; + *pid = 0; + } + + if (! *pid) unlink(fname); /* XXXXX race, race */ + + if (*pid) + DEBUG(5,("Read share file %s mode 0x%X pid=%d\n",fname,ret,*pid)); + + return(ret); +} + + +/******************************************************************* +del the share mode of a file, if we set it last +********************************************************************/ +void del_share_mode(int fnum) +{ + pstring fname; + int fd2; + char buf[16]; + time_t t=0; + int pid=0; + BOOL del = False; + + if (!share_name_fnum(fnum,fname)) return; + + fd2 = open(fname,O_RDONLY,0); + if (fd2 < 0) return; + if (read(fd2,buf,16) != 16) + del = True; + close(fd2); + + if (!del) { + t = IVAL(buf,0); + pid = IVAL(buf,8); + } + + if (!del) + if (IVAL(buf,12) != LOCKING_VERSION || !pid || !process_exists(pid)) + del = True; + + if (!del && t == Files[fnum].open_time && pid==(int)getpid()) + del = True; + + if (del) { + if (!unlink(fname)) + DEBUG(2,("Deleted share file %s\n",fname)); + else { + DEBUG(3,("Pending delete share file %s\n",fname)); + if (*share_del_pending) DEBUG(0,("Share del clash!\n")); + strcpy(share_del_pending,fname); + } + } +} + + +/******************************************************************* +set the share mode of a file +********************************************************************/ +BOOL set_share_mode(int fnum,int mode) +{ + pstring fname; + int fd2; + char buf[16]; + int pid = (int)getpid(); + + if (!share_name_fnum(fnum,fname)) return(False); + + { + int old_umask = umask(0); + fd2 = open(fname,O_WRONLY|O_CREAT|O_TRUNC,0644); + umask(old_umask); + } + if (fd2 < 0) { + DEBUG(2,("Failed to create share file %s\n",fname)); + return(False); + } + + SIVAL(buf,0,Files[fnum].open_time); + SIVAL(buf,4,mode); + SIVAL(buf,8,pid); + SIVAL(buf,12,LOCKING_VERSION); + + if (write(fd2,buf,16) != 16) { + close(fd2); + unlink(fname); + return(False); + } + + write(fd2,Files[fnum].name,strlen(Files[fnum].name)+1); + + close(fd2); + + DEBUG(3,("Created share file %s with mode 0x%X pid=%d\n",fname,mode,pid)); + + return(True); +} + + +/******************************************************************* +cleanup any stale share files +********************************************************************/ +void clean_share_files(void) +{ + char *lockdir = lp_lockdir(); + void *dir; + char *s; + + if (!*lockdir) return; + + dir = opendir(lockdir); + if (!dir) return; + + while ((s=readdirname(dir))) { + char buf[16]; + int pid; + int fd; + pstring lname; + int dev,inode; + + if (sscanf(s,"share.%d.%d",&dev,&inode)!=2) continue; + + strcpy(lname,lp_lockdir()); + trim_string(lname,NULL,"/"); + strcat(lname,"/"); + strcat(lname,s); + + fd = open(lname,O_RDONLY,0); + if (fd < 0) continue; + + if (read(fd,buf,16) != 16) { + close(fd); + if (!unlink(lname)) + printf("Deleted corrupt share file %s\n",s); + continue; + } + close(fd); + + pid = IVAL(buf,8); + + if (IVAL(buf,12) != LOCKING_VERSION || !process_exists(pid)) { + if (!unlink(lname)) + printf("Deleted stale share file %s\n",s); + } + } + + closedir(dir); +} diff --git a/source3/md4.h b/source3/md4.h new file mode 100644 index 0000000000..3f60d75fe3 --- /dev/null +++ b/source3/md4.h @@ -0,0 +1,58 @@ +/* + This code is from rfc1186. +*/ + + /* + ** ******************************************************************** + ** md4.h -- Header file for implementation of ** + ** MD4 Message Digest Algorithm ** + ** Updated: 2/13/90 by Ronald L. Rivest ** + ** (C) 1990 RSA Data Security, Inc. ** + ** ******************************************************************** + */ + + /* MDstruct is the data structure for a message digest computation. + */ + typedef struct { + unsigned int buffer[4]; /* Holds 4-word result of MD computation */ + unsigned char count[8]; /* Number of bits processed so far */ + unsigned int done; /* Nonzero means MD computation finished */ + } MDstruct, *MDptr; + + /* MDbegin(MD) + + + + ** Input: MD -- an MDptr + ** Initialize the MDstruct prepatory to doing a message digest + ** computation. + */ + extern void MDbegin(); + + /* MDupdate(MD,X,count) + ** Input: MD -- an MDptr + ** X -- a pointer to an array of unsigned characters. + ** count -- the number of bits of X to use (an unsigned int). + ** Updates MD using the first "count" bits of X. + ** The array pointed to by X is not modified. + ** If count is not a multiple of 8, MDupdate uses high bits of + ** last byte. + ** This is the basic input routine for a user. + ** The routine terminates the MD computation when count < 512, so + ** every MD computation should end with one call to MDupdate with a + ** count less than 512. Zero is OK for a count. + */ + extern void MDupdate(); + + /* MDprint(MD) + ** Input: MD -- an MDptr + ** Prints message digest buffer MD as 32 hexadecimal digits. + ** Order is from low-order byte of buffer[0] to high-order byte + ** of buffer[3]. + ** Each byte is printed with high-order hexadecimal digit first. + */ + extern void MDprint(); + + /* + ** End of md4.h + */ diff --git a/source3/nameserv.c b/source3/nameserv.c new file mode 100644 index 0000000000..802b98ec0a --- /dev/null +++ b/source3/nameserv.c @@ -0,0 +1,2318 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1995 + + 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. + +*/ + +#include "includes.h" +#include "loadparm.h" +#include "nameserv.h" + + +static void queue_packet(struct packet_struct *packet); +void process(void); +static void dump_names(void); +static void announce_request(char *group); +void sync_browse_lists(char *name,int name_type,char *myname, + char *domain,struct in_addr ip); + +extern int DEBUGLEVEL; + +extern pstring debugf; +pstring servicesf = CONFIGFILE; + +extern pstring scope; + +extern BOOL CanRecurse; + +extern struct in_addr myip; +extern struct in_addr bcast_ip; +extern struct in_addr Netmask; +extern pstring myhostname; +static pstring host_file; +static pstring myname=""; + +static int ClientNMB= -1; +static int ClientDGRAM= -1; + +static BOOL needannounce=True; + +/* this is our name database */ +static struct name_record *namelist = NULL; + +/* list of servers to be returned by NetServerEnum */ +static struct server_record *serverlist = NULL; + +/* this is the domain list. For the moment we will assume that our + primary domain is the first one listed in this list */ +static struct domain_record *domainlist = NULL; + +/* are we running as a daemon ? */ +static BOOL is_daemon = False; + +/* machine comment for host announcements */ +static pstring ServerComment=""; + +static BOOL got_bcast = False; +static BOOL got_myip = False; +static BOOL got_nmask = False; + +static BOOL updatedlists = False; +static int updatecount=0; + +/* what server type are we currently */ +static int ServerType = +SV_TYPE_WORKSTATION | SV_TYPE_SERVER | SV_TYPE_TIME_SOURCE | +SV_TYPE_SERVER_UNIX | +SV_TYPE_PRINTQ_SERVER | SV_TYPE_POTENTIAL_BROWSER; + +/* here are my election parameters */ + +/* NTAS uses 2, NT uses 1, WfWg uses 0 */ +#define MAINTAIN_LIST 1 +#define ELECTION_VERSION 1 + +static BOOL RunningElection = False; +static BOOL needelection = False; +static int ElectionCount = 0; +static int StartupTime =0; + + +/* WfWg uses 01040b01 */ +/* Win95 uses 01041501 */ +/* NTAS uses ?? */ +static uint32 ElectionCriterion = (MAINTAIN_LIST<<1)|(ELECTION_VERSION<<8); + +/* we currently support being the master for just one group. Being the + master for more than one group might be tricky as NetServerEnum is + often asked for a list without naming the group */ +static fstring PrimaryGroup=""; + +#define AM_MASTER (PrimaryGroup[0] && (ServerType & SV_TYPE_MASTER_BROWSER)) + +#define MSBROWSE "\001\002__MSBROWSE__\002" + +#define GET_TTL(ttl) ((ttl)?MIN(ttl,lp_max_ttl()):lp_max_ttl()) + +#define BROWSE_MAILSLOT "\\MAILSLOT\\BROWSE" + +/**************************************************************************** +catch a sighup +****************************************************************************/ +static int sig_hup() +{ + BlockSignals(True); + + DEBUG(0,("Got SIGHUP (reload not implemented)\n")); + dump_names(); + reload_services(True); + + BlockSignals(False); +#ifndef DONT_REINSTALL_SIG + signal(SIGHUP,SIGNAL_CAST sig_hup); +#endif + return(0); +} + +/**************************************************************************** +catch a sigpipe +****************************************************************************/ +static int sig_pipe() +{ + BlockSignals(True); + + DEBUG(0,("Got SIGPIPE\n")); + if (!is_daemon) + exit(1); + BlockSignals(False); + return(0); +} + +#if DUMP_CORE +/******************************************************************* +prepare to dump a core file - carefully! +********************************************************************/ +static BOOL dump_core(void) +{ + char *p; + pstring dname; + strcpy(dname,debugf); + if ((p=strrchr(dname,'/'))) *p=0; + strcat(dname,"/corefiles"); + mkdir(dname,0700); + sys_chown(dname,getuid(),getgid()); + chmod(dname,0700); + if (chdir(dname)) return(False); + umask(~(0700)); + +#ifndef NO_GETRLIMIT +#ifdef RLIMIT_CORE + { + struct rlimit rlp; + getrlimit(RLIMIT_CORE, &rlp); + rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); + setrlimit(RLIMIT_CORE, &rlp); + getrlimit(RLIMIT_CORE, &rlp); + DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); + } +#endif +#endif + + + DEBUG(0,("Dumping core in %s\n",dname)); + return(True); +} +#endif + + +/**************************************************************************** +possibly continue after a fault +****************************************************************************/ +static void fault_continue(void) +{ + static int errcount=1; + + errcount--; + + if (is_daemon && errcount) + process(); + +#if DUMP_CORE + if (dump_core()) return; +#endif + + return; +} + + +/******************************************************************* + wrapper to get the DC + ******************************************************************/ +static char *domain_controller(void) +{ + char *dc = lp_domain_controller(); + /* so many people mistake this for a bool that we need to handle it. sigh. */ + if (!*dc || strequal(dc,"yes") || strequal(dc,"true")) + strcpy(dc,myname); + return(dc); +} + + + +/**************************************************************************** + true if two netbios names are equal +****************************************************************************/ +static BOOL name_equal(struct nmb_name *n1,struct nmb_name *n2) +{ + if (n1->name_type != n2->name_type) return(False); + + return(strequal(n1->name,n2->name) && strequal(n1->scope,n2->scope)); +} + +/**************************************************************************** + add a netbios name into the namelist + **************************************************************************/ +static void add_name(struct name_record *n) +{ + struct name_record *n2; + + if (!namelist) { + namelist = n; + n->prev = NULL; + n->next = NULL; + return; + } + + for (n2 = namelist; n2->next; n2 = n2->next) ; + + n2->next = n; + n->next = NULL; + n->prev = n2; +} + +/**************************************************************************** + add a domain into the list + **************************************************************************/ +static void add_domain(struct domain_record *d) +{ + struct domain_record *d2; + + if (!domainlist) { + domainlist = d; + d->prev = NULL; + d->next = NULL; + return; + } + + for (d2 = domainlist; d2->next; d2 = d2->next) ; + + d2->next = d; + d->next = NULL; + d->prev = d2; +} + + +/**************************************************************************** + add a server into the list + **************************************************************************/ +static void add_server(struct server_record *s) +{ + struct server_record *s2; + + if (!serverlist) { + serverlist = s; + s->prev = NULL; + s->next = NULL; + return; + } + + for (s2 = serverlist; s2->next; s2 = s2->next) ; + + s2->next = s; + s->next = NULL; + s->prev = s2; +} + +/**************************************************************************** + remove a name from the namelist. The pointer must be an element just + retrieved + **************************************************************************/ +static void remove_name(struct name_record *n) +{ + struct name_record *nlist = namelist; + while (nlist && nlist != n) nlist = nlist->next; + if (nlist) { + if (nlist->next) nlist->next->prev = nlist->prev; + if (nlist->prev) nlist->prev->next = nlist->next; + free(nlist); + } +} + +/**************************************************************************** + find a name in the namelist + **************************************************************************/ +static struct name_record *find_name(struct nmb_name *n) +{ + struct name_record *ret; + for (ret = namelist; ret; ret = ret->next) + if (name_equal(&ret->name,n)) return(ret); + + return(NULL); +} + +/**************************************************************************** + dump a copy of the name table + **************************************************************************/ +static void dump_names(void) +{ + time_t t = time(NULL); + struct name_record *n; + struct domain_record *d; + + DEBUG(3,("Dump of local name table:\n")); + + for (n = namelist; n; n = n->next) { + DEBUG(3,("%s %s TTL=%d Unique=%s\n", + namestr(&n->name), + inet_ntoa(n->ip), + n->death_time?n->death_time-t:0, + BOOLSTR(n->unique))); + } + + DEBUG(3,("\nDump of domain list:\n")); + for (d = domainlist; d; d = d->next) + DEBUG(3,("%s %s\n",d->name,inet_ntoa(d->bcast_ip))); +} + + +/**************************************************************************** + add a host entry to the name list + ****************************************************************************/ +static struct name_record *add_host_entry(char *name,int type,BOOL unique,int ttl, + enum name_source source, + struct in_addr ip) +{ + struct name_record *n; + struct name_record *n2=NULL; + + n = (struct name_record *)malloc(sizeof(*n)); + if (!n) return(NULL); + + bzero((char *)n,sizeof(*n)); + + make_nmb_name(&n->name,name,type,scope); + if ((n2=find_name(&n->name))) { + free(n); + n = n2; + } + + if (ttl) n->death_time = time(NULL)+ttl*3; + n->ip = ip; + n->unique = unique; + n->source = source; + + if (!n2) add_name(n); + + DEBUG(3,("Added host entry %s at %s ttl=%d unique=%s\n", + namestr(&n->name),inet_ntoa(ip),ttl,BOOLSTR(unique))); + + return(n); +} + + +/**************************************************************************** + add a domain entry + ****************************************************************************/ +static struct domain_record *add_domain_entry(char *name,struct in_addr ip) +{ + struct domain_record *d; + + d = (struct domain_record *)malloc(sizeof(*d)); + + if (!d) return(NULL); + + bzero((char *)d,sizeof(*d)); + + if (zero_ip(ip)) ip = bcast_ip; + + StrnCpy(d->name,name,sizeof(d->name)-1); + d->bcast_ip = ip; + + if (!PrimaryGroup[0] && ip_equal(bcast_ip,ip) && name[0] != '*') { + strcpy(PrimaryGroup,name); + strupper(PrimaryGroup); + DEBUG(3,("Setting primary group to %s (%s)\n",PrimaryGroup,inet_ntoa(ip))); + } + + add_domain(d); + + ip = *interpret_addr2("255.255.255.255"); + if (name[0] != '*') add_host_entry(name,0x1e,False,0,SELF,ip); + + DEBUG(3,("Added domain entry %s at %s\n", + name,inet_ntoa(ip))); + + return(d); +} + +/**************************************************************************** + add a server entry + ****************************************************************************/ +struct server_record *add_server_entry(char *name,int servertype, + int ttl,char *comment,BOOL replace) +{ + BOOL newentry=False; + struct server_record *s; + + for (s = serverlist; s; s = s->next) + if (strequal(name,s->name)) break; + + if (s && !replace) { + DEBUG(4,("Not replacing %s\n",name)); + return(s); + } + + updatedlists=True; + + if (!s) { + newentry = True; + s = (struct server_record *)malloc(sizeof(*s)); + + if (!s) return(NULL); + + bzero((char *)s,sizeof(*s)); + } + + /* update the entry */ + StrnCpy(s->name,name,sizeof(s->name)-1); + StrnCpy(s->comment,comment,sizeof(s->comment)-1); + s->servertype = servertype; + s->death_time = ttl?time(NULL)+ttl*3:0; + strupper(s->name); + if (s->servertype & SV_TYPE_DOMAIN_ENUM) strupper(s->comment); + + if (!newentry) return(s); + + add_server(s); + + if (newentry) { + DEBUG(3,("Added server entry %s of type %x (%s)\n", + name,servertype,comment)); + } else { + DEBUG(3,("Updated server entry %s of type %x (%s)\n", + name,servertype,comment)); + } + + return(s); +} + + +/**************************************************************************** + add the magic samba names, useful for finding samba servers + **************************************************************************/ +static void add_my_names(void) +{ + struct in_addr ip; + + ip = *interpret_addr2("0.0.0.0"); + + add_host_entry(myname,0x20,True,0,SELF,ip); + add_host_entry(myname,0x0,True,0,SELF,ip); + add_host_entry(myname,0x1f,True,0,SELF,ip); /* used for chat?? */ + add_host_entry(myname,0x3,True,0,SELF,ip); /* used for winpopup */ + + if (!domainlist) + add_domain_entry(lp_workgroup(),bcast_ip); + add_server_entry(myname, + ServerType, + 0,ServerComment,True); + + add_host_entry("__SAMBA__",0x20,True,0,SELF,ip); + add_host_entry("__SAMBA__",0x0,True,0,SELF,ip); + + if (lp_preferred_master()) { + DEBUG(3,("Preferred master startup\n")); + needelection = True; + ElectionCriterion |= (1<<3); + } + + ElectionCriterion |= (lp_os_level() << 24); +} + + +/******************************************************************* + write out browse.dat + ******************************************************************/ +static void write_browse_list(void) +{ + struct server_record *s; + pstring fname,fnamenew; + FILE *f; + + updatecount++; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + strcpy(fnamenew,fname); + strcat(fnamenew,"."); + + f = fopen(fnamenew,"w"); + + if (!f) { + DEBUG(4,("Can't open %s - %s\n",fnamenew,strerror(errno))); + return; + } + + for (s=serverlist; s ; s = s->next) { + /* don't list domains I don't have a master for */ + if ((s->servertype & SV_TYPE_DOMAIN_ENUM) && !s->comment[0]) continue; + + fprintf(f,"\"%s\"\t%08x\t\"%s\"\n",s->name,s->servertype,s->comment); + } + + + fclose(f); + chmod(fnamenew,0644); + /* unlink(fname); */ + rename(fnamenew,fname); + DEBUG(3,("Wrote browse list %s\n",fname)); +} + +/******************************************************************* + expire old names in the namelist and serverlist + ******************************************************************/ +static void expire_names(void) +{ + static time_t lastrun=0; + time_t t = time(NULL); + struct name_record *n; + struct name_record *next; + struct server_record *s; + struct server_record *nexts; + + if (!lastrun) lastrun = t; + if (t < lastrun + 5) return; + lastrun = t; + + /* expire old names */ + for (n = namelist; n; n = next) { + if (n->death_time && n->death_time < t) { + DEBUG(3,("Removing dead name %s\n", + namestr(&n->name))); + next = n->next; + if (n->prev) n->prev->next = n->next; + if (n->next) n->next->prev = n->prev; + if (namelist == n) namelist = n->next; + free(n); + } else { + next = n->next; + } + } + + /* expire old entries in the serverlist */ + for (s = serverlist; s; s = nexts) { + if (s->death_time && s->death_time < t) { + DEBUG(3,("Removing dead server %s\n",s->name)); + updatedlists = True; + nexts = s->next; + if (s->prev) s->prev->next = s->next; + if (s->next) s->next->prev = s->prev; + if (serverlist == s) serverlist = s->next; + free(s); + } else { + nexts = s->next; + } + } +} + + +/******************************************************************* + delete old names from the namelist + ******************************************************************/ +static void housekeeping(void) +{ + time_t t = time(NULL); + + expire_names(); + + /* write out the browse.dat database for smbd to get */ + if (updatedlists) { + write_browse_list(); + updatedlists = False; + } + + { + /* occasionally check to see if the master browser is around */ + static time_t lastrun=0; + if (!lastrun) lastrun = t; + if (t < lastrun + 5*60) return; + lastrun = t; + + if (!AM_MASTER && PrimaryGroup[0] && + !name_query(ClientNMB,PrimaryGroup,0x1d,True,False, + bcast_ip,NULL,queue_packet)) { + DEBUG(2,("Forcing election on %s\n",PrimaryGroup)); + needelection = True; + } + } +} + + +/**************************************************************************** + reload the services file + **************************************************************************/ +BOOL reload_services(BOOL test) +{ + BOOL ret; + extern fstring remote_machine; + + strcpy(remote_machine,"nmbd"); + + if (lp_loaded()) + { + pstring fname; + strcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) + { + strcpy(servicesf,fname); + test = False; + } + } + + if (test && !lp_file_list_changed()) + return(True); + + ret = lp_load(servicesf,True); + + /* perhaps the config filename is now set */ + if (!test) + reload_services(True); + + return(ret); +} + + + +/**************************************************************************** +load a netbios hosts file +****************************************************************************/ +static void load_hosts_file(char *fname) +{ + FILE *f = fopen(fname,"r"); + pstring line; + if (!f) { + DEBUG(2,("Can't open lmhosts file %s\n",fname)); + return; + } + + while (!feof(f)) + { + if (!fgets_slash(line,sizeof(pstring),f)) continue; + + if (*line == '#') continue; + + { + BOOL group=False; + string ip,name,flags,extra; + char *ptr; + int count = 0; + struct in_addr ipaddr; + enum name_source source = LMHOSTS; + + *ip = *name = *flags = *extra = 0; + + ptr = line; + + if (next_token(&ptr,ip,NULL)) ++count; + if (next_token(&ptr,name,NULL)) ++count; + if (next_token(&ptr,flags,NULL)) ++count; + if (next_token(&ptr,extra,NULL)) ++count; + + if (count <= 0) continue; + + if (count > 0 && count < 2) + { + DEBUG(0,("Ill formed hosts line [%s]\n",line)); + continue; + } + + if (strchr(flags,'G') || strchr(flags,'S')) + group = True; + + if (strchr(flags,'M') && !group) { + source = SELF; + strcpy(myname,name); + } + + ipaddr = *interpret_addr2(ip); + + if (group) { + add_domain_entry(name,ipaddr); + } else { + add_host_entry(name,0x20,True,0,source,ipaddr); + } + } + } + + fclose(f); +} + +/******************************************************************* + check if 2 IPs are on the same net + we will assume the local netmask, although this could be wrong XXXX + ******************************************************************/ +static BOOL same_net(struct in_addr ip1,struct in_addr ip2) +{ + unsigned long net1,net2,nmask; + + nmask = ntohl(Netmask.s_addr); + net1 = ntohl(ip1.s_addr); + net2 = ntohl(ip2.s_addr); + + return((net1 & nmask) == (net2 & nmask)); +} + +/**************************************************************************** + send an election packet + **************************************************************************/ +static void send_election(char *group,uint32 criterion,int timeup,char *name) +{ + pstring outbuf; + char *p; + + DEBUG(2,("Sending election to %s for workgroup %s\n", + inet_ntoa(bcast_ip),group)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = 8; /* election */ + p++; + + CVAL(p,0) = ELECTION_VERSION; + SIVAL(p,1,criterion); + SIVAL(p,5,timeup*1000); /* ms - despite the spec */ + p += 13; + strcpy(p,name); + strupper(p); + p = skip_string(p,1); + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + name,group,0,0x1e,bcast_ip,myip); +} + + +/**************************************************************************** + send a backup list response + **************************************************************************/ +static void send_backup_list(char *name,int token,struct nmb_name *to, + struct in_addr ip) +{ + pstring outbuf; + char *p; + + DEBUG(2,("Sending backup list to %s for workgroup %s\n", + inet_ntoa(ip),PrimaryGroup)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = 10; /* backup list response */ + p++; + + CVAL(p,0) = 1; /* count */ + SIVAL(p,1,token); + p += 5; + strcpy(p,name); + strupper(p); + p = skip_string(p,1) + 1; + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + myname,to->name,0,to->name_type,ip,myip); +} + + +/******************************************************************* + become the master browser + ******************************************************************/ +static void become_master(void) +{ + uint32 domain_type = SV_TYPE_DOMAIN_ENUM | SV_TYPE_SERVER_UNIX; + DEBUG(2,("Becoming master for %s\n",PrimaryGroup)); + + ServerType |= SV_TYPE_MASTER_BROWSER; + ServerType |= SV_TYPE_BACKUP_BROWSER; + ElectionCriterion |= 0x5; + + add_host_entry(PrimaryGroup,0x1d,True,0,SELF,myip); + add_host_entry(PrimaryGroup,0x0,False,0,SELF,myip); + add_host_entry(MSBROWSE,1,False,0,SELF,myip); + + if (lp_domain_master()) { + add_host_entry(myname,0x1b,True,0,SELF,myip); + add_host_entry(PrimaryGroup,0x1b,True,0,SELF,myip); + add_host_entry(PrimaryGroup,0x1c,False,0,SELF,myip); + ServerType |= SV_TYPE_DOMAIN_MASTER; + if (lp_domain_logons()) { + ServerType |= SV_TYPE_DOMAIN_CTRL; + ServerType |= SV_TYPE_DOMAIN_MEMBER; + domain_type |= SV_TYPE_DOMAIN_CTRL; + } + } + + add_server_entry(PrimaryGroup,domain_type,0,myname,True); + add_server_entry(myname,ServerType,0,ServerComment,True); + + announce_request(PrimaryGroup); + + needannounce = True; +} + + +/******************************************************************* + unbecome the master browser + ******************************************************************/ +static void become_nonmaster(void) +{ + struct name_record *n; + struct nmb_name nn; + + DEBUG(2,("Becoming non-master for %s\n",PrimaryGroup)); + + ServerType &= ~SV_TYPE_MASTER_BROWSER; + ServerType &= ~SV_TYPE_DOMAIN_CTRL; + ServerType &= ~SV_TYPE_DOMAIN_MASTER; + + ElectionCriterion &= ~0x4; + + make_nmb_name(&nn,PrimaryGroup,0x1d,scope); + n = find_name(&nn); + if (n && n->source == SELF) remove_name(n); + + make_nmb_name(&nn,PrimaryGroup,0x1b,scope); + n = find_name(&nn); + if (n && n->source == SELF) remove_name(n); + + make_nmb_name(&nn,MSBROWSE,1,scope); + n = find_name(&nn); + if (n && n->source == SELF) remove_name(n); +} + + +/******************************************************************* + run the election + ******************************************************************/ +static void run_election(void) +{ + time_t t = time(NULL); + static time_t lastime = 0; + + if (!PrimaryGroup[0] || !RunningElection) return; + + /* send election packets once a second */ + if (lastime && + t-lastime <= 0) return; + + lastime = t; + + send_election(PrimaryGroup,ElectionCriterion,t-StartupTime,myname); + + if (ElectionCount++ < 4) return; + + /* I won! now what :-) */ + RunningElection = False; + DEBUG(2,(">>> Won election on %s <<<\n",PrimaryGroup)); + become_master(); +} + + +/**************************************************************************** + construct a host announcement unicast + **************************************************************************/ +static void announce_host(struct domain_record *d,char *my_name,char *comment) +{ + time_t t = time(NULL); + pstring outbuf; + char *p; + char *namep; + char *stypep; + char *commentp; + uint32 stype = ServerType; + + if (needannounce) { + /* drop back to a max 3 minute announce - this is to prevent a + single lost packet from stuffing things up for too long */ + d->announce_interval = MIN(d->announce_interval,3*60); + d->lastannounce_time = t - (d->announce_interval+1); + } + + /* announce every minute at first then progress to every 12 mins */ + if (d->lastannounce_time && + (t - d->lastannounce_time) < d->announce_interval) + return; + + if (d->announce_interval < 12*60) d->announce_interval += 60; + d->lastannounce_time = t; + + DEBUG(2,("Sending announcement to %s for workgroup %s\n", + inet_ntoa(d->bcast_ip),d->name)); + + if (!strequal(PrimaryGroup,d->name) || + !ip_equal(bcast_ip,d->bcast_ip)) { + stype &= ~(SV_TYPE_POTENTIAL_BROWSER | SV_TYPE_MASTER_BROWSER | + SV_TYPE_DOMAIN_MASTER | SV_TYPE_BACKUP_BROWSER | + SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_MEMBER); + } + + if (!*comment) comment = "NoComment"; + if (!*my_name) my_name = "NoName"; + + if (strlen(comment) > 43) comment[43] = 0; + + bzero(outbuf,sizeof(outbuf)); + CVAL(outbuf,0) = 1; /* host announce */ + p = outbuf+1; + + CVAL(p,0) = updatecount; + SIVAL(p,1,d->announce_interval*1000); /* ms - despite the spec */ + namep = p+5; + StrnCpy(p+5,my_name,16); + strupper(p+5); + CVAL(p,21) = 2; /* major version */ + CVAL(p,22) = 2; /* minor version */ + stypep = p+23; + SIVAL(p,23,stype); + SSVAL(p,27,0xaa55); /* browse signature */ + SSVAL(p,29,1); /* browse version */ + commentp = p+31; + strcpy(p+31,comment); + p += 31; + p = skip_string(p,1); + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + my_name,d->name,0,0x1d,d->bcast_ip,myip); + + /* if I'm the master then I also need to do a local master and + domain announcement */ + + if (AM_MASTER && + strequal(d->name,PrimaryGroup) && + ip_equal(bcast_ip,d->bcast_ip)) { + + /* do master announcements as well */ + SIVAL(stypep,0,ServerType); + + CVAL(outbuf,0) = 15; /* local master announce */ + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + my_name,PrimaryGroup,0,0x1e,d->bcast_ip,myip); + + CVAL(outbuf,0) = 12; /* domain announce */ + StrnCpy(namep,PrimaryGroup,15); + strupper(namep); + StrnCpy(commentp,myname,15); + strupper(commentp); + SIVAL(stypep,0,(unsigned)0x80000000); + p = commentp + strlen(commentp) + 1; + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + my_name,MSBROWSE,0,1,d->bcast_ip,myip); + } +} + + +/**************************************************************************** + send a announce request to the local net + **************************************************************************/ +static void announce_request(char *group) +{ + pstring outbuf; + char *p; + + DEBUG(2,("Sending announce request to %s for workgroup %s\n", + inet_ntoa(bcast_ip),group)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = 2; /* announce request */ + p++; + + CVAL(p,0) = 0; /* flags?? */ + p++; + StrnCpy(p,myname,16); + strupper(p); + p = skip_string(p,1); + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + myname,group,0,0,bcast_ip,myip); +} + +/**************************************************************************** + announce myself as a master to the PDC + **************************************************************************/ +static void announce_master(char *group) +{ + static time_t last=0; + time_t t = time(NULL); + pstring outbuf; + char *p; + struct in_addr ip,pdc_ip; + fstring pdcname; + *pdcname = 0; + + if (strequal(domain_controller(),myname)) return; + + if (!AM_MASTER || (last && (t-last < 10*60))) return; + last = t; + + ip = *interpret_addr2(domain_controller()); + + if (zero_ip(ip)) ip = bcast_ip; + + if (!name_query(ClientNMB,PrimaryGroup, + 0x1b,False,False,ip,&pdc_ip,queue_packet)) { + DEBUG(2,("Failed to find PDC at %s\n",domain_controller())); + return; + } + + name_status(ClientNMB,PrimaryGroup,0x1b,False, + pdc_ip,NULL,pdcname,queue_packet); + + if (!pdcname[0]) { + DEBUG(3,("Can't find netbios name of PDC at %s\n",inet_ntoa(pdc_ip))); + } else { + sync_browse_lists(pdcname,0x20,myname,PrimaryGroup,pdc_ip); + } + + + DEBUG(2,("Sending master announce to %s for workgroup %s\n", + inet_ntoa(pdc_ip),group)); + + bzero(outbuf,sizeof(outbuf)); + p = outbuf; + CVAL(p,0) = 13; /* announce request */ + p++; + + StrnCpy(p,myname,16); + strupper(p); + p = skip_string(p,1); + + send_mailslot_reply(BROWSE_MAILSLOT,ClientDGRAM,outbuf,PTR_DIFF(p,outbuf), + myname,PrimaryGroup,0x1b,0,pdc_ip,myip); +} + + +/******************************************************************* + am I listening on a name. Should check name_type as well + + This is primarily used to prevent us gathering server lists from + other workgroups we aren't a part of + ******************************************************************/ +static BOOL listening(struct nmb_name *n) +{ + if (!strequal(n->scope,scope)) return(False); + + if (strequal(n->name,myname) || + strequal(n->name,PrimaryGroup) || + strequal(n->name,MSBROWSE)) + return(True); + + return(False); +} + + +/******************************************************************* + process a domain announcement frame + + Announce frames come in 3 types. Servers send host announcements + (command=1) to let the master browswer know they are + available. Master browsers send local master announcements + (command=15) to let other masters and backups that they are the + master. They also send domain announcements (command=12) to register + the domain + + The comment field of domain announcements contains the master + browser name. The servertype is used by NetServerEnum to select + resources. We just have to pass it to smbd (via browser.dat) and let + the client choose using bit masks. + ******************************************************************/ +static void process_announce(struct packet_struct *p,int command,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int update_count = CVAL(buf,0); + int ttl = IVAL(buf,1)/1000; + char *name = buf+5; + int osmajor=CVAL(buf,21); + int osminor=CVAL(buf,22); + uint32 servertype = IVAL(buf,23); + char *comment = buf+31; + + name[15] = 0; + comment[43] = 0; + + DEBUG(3,("Announce(%d) %s count=%d ttl=%d OS=(%d,%d) type=%08x comment=%s\n", + command,name,update_count,ttl,osmajor,osminor, + servertype,comment)); + + if (strequal(dgram->source_name.name,myname)) return; + + if (!listening(&dgram->dest_name)) return; + + ttl = GET_TTL(ttl); + + /* add them to our browse list */ + add_server_entry(name,servertype,ttl,comment,True); + +} + +/******************************************************************* + process a master announcement frame + ******************************************************************/ +static void process_master_announce(struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + char *name = buf; + + name[15] = 0; + + DEBUG(3,("Master Announce from %s (%s)\n",name,inet_ntoa(p->ip))); + + if (strequal(dgram->source_name.name,myname)) return; + + if (!AM_MASTER || !listening(&dgram->dest_name)) return; + + /* merge browse lists with them */ + if (lp_domain_master()) + sync_browse_lists(name,0x20,myname,PrimaryGroup,p->ip); +} + + +/******************************************************************* + process a backup list request + + A client send a backup list request to ask for a list of servers on + the net that maintain server lists for a domain. A server is then + chosen from this list to send NetServerEnum commands to to list + available servers. + + Currently samba only sends back one name in the backup list, its + wn. For larger nets we'll have to add backups and send "become + backup" requests occasionally. + ******************************************************************/ +static void process_backup_list(struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int count = CVAL(buf,0); + int token = IVAL(buf,1); + + DEBUG(3,("Backup request to %s token=%d\n", + namestr(&dgram->dest_name), + token)); + + if (strequal(dgram->source_name.name,myname)) return; + + if (count <= 0) return; + + if (!AM_MASTER || + !strequal(PrimaryGroup,dgram->dest_name.name)) + return; + + if (!listening(&dgram->dest_name)) return; + + send_backup_list(myname,token, + &dgram->source_name, + p->ip); +} + + +/******************************************************************* + work out if I win an election + ******************************************************************/ +static BOOL win_election(int version,uint32 criterion,int timeup,char *name) +{ + time_t t = time(NULL); + uint32 mycriterion; + if (version > ELECTION_VERSION) return(False); + if (version < ELECTION_VERSION) return(True); + + mycriterion = ElectionCriterion; + + if (criterion > mycriterion) return(False); + if (criterion < mycriterion) return(True); + + if (timeup > (t - StartupTime)) return(False); + if (timeup < (t - StartupTime)) return(True); + + if (strcasecmp(myname,name) > 0) return(False); + + return(True); +} + + +/******************************************************************* + process a election packet + + An election dynamically decides who will be the master. + ******************************************************************/ +static void process_election(struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int version = CVAL(buf,0); + uint32 criterion = IVAL(buf,1); + int timeup = IVAL(buf,5)/1000; + char *name = buf+13; + + name[15] = 0; + + DEBUG(3,("Election request from %s vers=%d criterion=%08x timeup=%d\n", + name,version,criterion,timeup)); + + if (strequal(dgram->source_name.name,myname)) return; + + if (!listening(&dgram->dest_name)) return; + + if (win_election(version,criterion,timeup,name)) { + if (!RunningElection) { + needelection = True; + ElectionCount=0; + } + } else { + needelection = False; + if (RunningElection) { + RunningElection = False; + DEBUG(3,(">>> Lost election on %s <<<\n",PrimaryGroup)); + + /* if we are the master then remove our masterly names */ + if (AM_MASTER) + become_nonmaster(); + } + } +} + + +/******************************************************************* + process a announcement request + + clients send these when they want everyone to send an announcement + immediately. This can cause quite a storm of packets! + ******************************************************************/ +static void process_announce_request(struct packet_struct *p,char *buf) +{ + struct dgram_packet *dgram = &p->packet.dgram; + int flags = CVAL(buf,0); + char *name = buf+1; + + name[15] = 0; + + DEBUG(3,("Announce request from %s flags=0x%X\n",name,flags)); + + if (strequal(dgram->source_name.name,myname)) return; + + needannounce = True; +} + + +/**************************************************************************** +process a browse frame +****************************************************************************/ +static void process_browse_packet(struct packet_struct *p,char *buf,int len) +{ + int command = CVAL(buf,0); + switch (command) + { + case 1: /* host announce */ + case 12: /* domain announce */ + case 15: /* local master announce */ + process_announce(p,command,buf+1); + break; + + case 2: /* announce request */ + process_announce_request(p,buf+1); + break; + + case 8: /* election */ + process_election(p,buf+1); + break; + + case 9: /* get backup list */ + process_backup_list(p,buf+1); + break; + + case 13: /* master announcement */ + process_master_announce(p,buf+1); + break; + } +} + + +/**************************************************************************** + process a domain logon packet + **************************************************************************/ +static void process_logon_packet(struct packet_struct *p,char *buf,int len) +{ + char *logname,*q; + pstring outbuf; + struct dgram_packet *dgram = &p->packet.dgram; + int code; + + if (!lp_domain_logons()) { + DEBUG(3,("No domain logons\n")); + return; + } + if (!listening(&dgram->dest_name)) { + DEBUG(4,("Not listening to that domain\n")); + return; + } + + q = outbuf; + bzero(outbuf,sizeof(outbuf)); + + code = SVAL(buf,0); + switch (code) { + case 0: + { + char *machine = buf+2; + char *user = skip_string(machine,1); + logname = skip_string(user,1); + + SSVAL(q,0,6); + q += 2; + strcpy(q,"\\\\"); + q += 2; + StrnCpy(q,myname,16); + strupper(q); + q = skip_string(q,1); + SSVAL(q,0,0xFFFF); + q += 2; + + DEBUG(3,("Domain login request from %s(%s) user=%s\n", + machine,inet_ntoa(p->ip),user)); + } + break; + case 7: + { + char *machine = buf+2; + logname = skip_string(machine,1); + + SSVAL(q,0,0xc); + q += 2; + StrnCpy(q,domain_controller(),16); + strupper(q); + q = skip_string(q,1); + q += PutUniCode(q,domain_controller()); + q += PutUniCode(q,dgram->dest_name.name); + SSVAL(q,0,0xFFFF); + q += 2; + + DEBUG(3,("GETDC request from %s(%s)\n", + machine,inet_ntoa(p->ip))); + } + break; + default: + DEBUG(3,("Unknown domain request %d\n",code)); + return; + } + + + send_mailslot_reply(logname,ClientDGRAM,outbuf,PTR_DIFF(q,outbuf), + myname,&dgram->source_name.name[0],0,0,p->ip,myip); +} + +/**************************************************************************** +process udp 138 datagrams +****************************************************************************/ +static void process_dgram(struct packet_struct *p) +{ + char *buf; + char *buf2; + int len; + struct dgram_packet *dgram = &p->packet.dgram; + + if (dgram->header.msg_type != 0x10 && + dgram->header.msg_type != 0x11 && + dgram->header.msg_type != 0x12) { + /* don't process error packets etc yet */ + return; + } + + buf = &dgram->data[0]; + buf -= 4; /* XXXX for the pseudo tcp length - + someday I need to get rid of this */ + + if (CVAL(buf,smb_com) != SMBtrans) return; + + len = SVAL(buf,smb_vwv11); + buf2 = smb_base(buf) + SVAL(buf,smb_vwv12); + + DEBUG(3,("datagram from %s to %s for %s of type %d len=%d\n", + namestr(&dgram->source_name),namestr(&dgram->dest_name), + smb_buf(buf),CVAL(buf2,0),len)); + + if (len <= 0) return; + + if (strequal(smb_buf(buf),"\\MAILSLOT\\BROWSE")) { + process_browse_packet(p,buf2,len); + } else if (strequal(smb_buf(buf),"\\MAILSLOT\\NET\\NETLOGON")) { + process_logon_packet(p,buf2,len); + } + +} + +/******************************************************************* + find a workgroup using the specified broadcast + ******************************************************************/ +static BOOL find_workgroup(char *name,struct in_addr ip) +{ + fstring name1; + BOOL ret; + struct in_addr ipout; + + strcpy(name1,MSBROWSE); + + ret = name_query(ClientNMB,name1,0x1,True,False,ip,&ipout,queue_packet); + if (!ret) return(False); + + name_status(ClientNMB,name1,0x1,False,ipout,name,NULL,queue_packet); + + if (name[0] != '*') { + DEBUG(2,("Found workgroup %s on broadcast %s\n",name,inet_ntoa(ip))); + } else { + DEBUG(3,("Failed to find workgroup %s on broadcast %s\n",name,inet_ntoa(ip))); + } + return(name[0] != '*'); +} + + +/**************************************************************************** + a hook for announce handling - called every minute + **************************************************************************/ +static void do_announcements(void) +{ + struct domain_record *d; + + for (d = domainlist; d; d = d->next) { + /* if the ip address is 0 then set to the broadcast */ + if (zero_ip(d->bcast_ip)) d->bcast_ip = bcast_ip; + + /* if the workgroup is '*' then find a workgroup to be part of */ + if (d->name[0] == '*') { + if (!find_workgroup(d->name,d->bcast_ip)) continue; + add_host_entry(d->name,0x1e,False,0,SELF, + *interpret_addr2("255.255.255.255")); + if (!PrimaryGroup[0] && ip_equal(bcast_ip,d->bcast_ip)) { + strcpy(PrimaryGroup,d->name); + strupper(PrimaryGroup); + } + } + + announce_host(d,myname,ServerComment); + } + + /* if I have a domain controller then announce to it */ + if (AM_MASTER) + announce_master(PrimaryGroup); + + needannounce=False; +} + +/******************************************************************* + check if someone still owns a name + ******************************************************************/ +static BOOL confirm_name(struct name_record *n) +{ + struct in_addr ipout; + BOOL ret = name_query(ClientNMB,n->name.name, + n->name.name_type,False, + False,n->ip,&ipout,queue_packet); + return(ret && ip_equal(ipout,n->ip)); +} + +/**************************************************************************** +reply to a name release +****************************************************************************/ +static void reply_name_release(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct packet_struct p2; + struct nmb_packet *nmb2; + struct res_rec answer_rec; + struct in_addr ip; + int rcode=0; + int nb_flags = nmb->additional->rdata[0]; + BOOL bcast = nmb->header.nm_flags.bcast; + + + putip((char *)&ip,&nmb->additional->rdata[2]); + + { + struct name_record *n = find_name(&nmb->question.question_name); + if (n && n->unique && n->source == REGISTER && + ip_equal(ip,n->ip)) { + remove_name(n); n = NULL; + } + + /* XXXX under what conditions should we reject the removal?? */ + } + + DEBUG(3,("Name release on name %s rcode=%d\n", + namestr(&nmb->question.question_name),rcode)); + + if (bcast) return; + + /* Send a NAME RELEASE RESPONSE */ + p2 = *p; + nmb2 = &p2.packet.nmb; + + nmb2->header.response = True; + nmb2->header.nm_flags.bcast = False; + nmb2->header.nm_flags.recursion_available = CanRecurse; + nmb2->header.nm_flags.trunc = False; + nmb2->header.nm_flags.authoritative = True; + nmb2->header.qdcount = 0; + nmb2->header.ancount = 1; + nmb2->header.nscount = 0; + nmb2->header.arcount = 0; + nmb2->header.rcode = rcode; + + nmb2->answers = &answer_rec; + bzero((char *)nmb2->answers,sizeof(*nmb2->answers)); + + nmb2->answers->rr_name = nmb->question.question_name; + nmb2->answers->rr_type = nmb->question.question_type; + nmb2->answers->rr_class = nmb->question.question_class; + nmb2->answers->ttl = 0; + nmb2->answers->rdlength = 6; + nmb2->answers->rdata[0] = nb_flags; + putip(&nmb2->answers->rdata[2],(char *)&ip); + + send_packet(&p2); +} + +/**************************************************************************** + reply to a reg request + **************************************************************************/ +static void reply_name_reg(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + BOOL wildcard = (qname[0] == '*'); + BOOL bcast = nmb->header.nm_flags.bcast; + int ttl = GET_TTL(nmb->additional->ttl); + int name_type = nmb->question.question_name.name_type; + int nb_flags = nmb->additional->rdata[0]; + struct packet_struct p2; + struct nmb_packet *nmb2; + struct res_rec answer_rec; + struct in_addr ip; + BOOL group = (nb_flags&0x80)?True:False; + int rcode = 0; + + if (wildcard) return; + + putip((char *)&ip,&nmb->additional->rdata[2]); + + if (group) { + /* apparently we should return 255.255.255.255 for group queries (email from MS) */ + ip = *interpret_addr2("255.255.255.255"); + } + + { + struct name_record *n = find_name(&nmb->question.question_name); + + if (n) { + if (!group && !ip_equal(ip,n->ip)) { + /* check if the previous owner still wants it, + if so reject the registration, otherwise change the owner + and refresh */ + if (n->source != REGISTER || confirm_name(n)) { + rcode = 6; + } else { + n->ip = ip; + n->death_time = ttl?p->timestamp+ttl*3:0; + DEBUG(3,("%s changed owner to %s\n", + namestr(&n->name),inet_ntoa(n->ip))); + } + } else { + /* refresh the name */ + if (n->source != SELF) + n->death_time = ttl?p->timestamp + ttl*3:0; + } + } else { + /* add the name to our database */ + n = add_host_entry(qname,name_type,!group,ttl,REGISTER,ip); + } + } + + if (bcast) return; + + DEBUG(3,("Name registration for name %s at %s rcode=%d\n", + namestr(&nmb->question.question_name), + inet_ntoa(ip),rcode)); + + /* Send a NAME REGISTRATION RESPONSE */ + /* a lot of fields get copied from the query. This gives us the IP + and port the reply will be sent to etc */ + p2 = *p; + nmb2 = &p2.packet.nmb; + + nmb2->header.opcode = 5; + nmb2->header.response = True; + nmb2->header.nm_flags.bcast = False; + nmb2->header.nm_flags.recursion_available = CanRecurse; + nmb2->header.nm_flags.trunc = False; + nmb2->header.nm_flags.authoritative = True; + nmb2->header.qdcount = 0; + nmb2->header.ancount = 1; + nmb2->header.nscount = 0; + nmb2->header.arcount = 0; + nmb2->header.rcode = rcode; + + nmb2->answers = &answer_rec; + bzero((char *)nmb2->answers,sizeof(*nmb2->answers)); + + nmb2->answers->rr_name = nmb->question.question_name; + nmb2->answers->rr_type = nmb->question.question_type; + nmb2->answers->rr_class = nmb->question.question_class; + + nmb2->answers->ttl = ttl; + nmb2->answers->rdlength = 6; + nmb2->answers->rdata[0] = nb_flags; + putip(&nmb2->answers->rdata[2],(char *)&ip); + + send_packet(&p2); +} + + +/**************************************************************************** +reply to a name status query +****************************************************************************/ +static void reply_name_status(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + BOOL wildcard = (qname[0] == '*'); + struct packet_struct p2; + struct nmb_packet *nmb2; + struct res_rec answer_rec; + char *buf; + int count; + int rcode = 0; + struct name_record *n = find_name(&nmb->question.question_name); + + DEBUG(3,("Name status for name %s\n", + namestr(&nmb->question.question_name))); + + if (!wildcard && (!n || n->source != SELF)) + return; + + /* Send a POSITIVE NAME STATUS RESPONSE */ + /* a lot of fields get copied from the query. This gives us the IP + and port the reply will be sent to etc */ + p2 = *p; + nmb2 = &p2.packet.nmb; + + nmb2->header.response = True; + nmb2->header.nm_flags.bcast = False; + nmb2->header.nm_flags.recursion_available = CanRecurse; + nmb2->header.nm_flags.trunc = False; + nmb2->header.nm_flags.authoritative = True; /* WfWg ignores + non-authoritative answers */ + nmb2->header.qdcount = 0; + nmb2->header.ancount = 1; + nmb2->header.nscount = 0; + nmb2->header.arcount = 0; + nmb2->header.rcode = rcode; + + nmb2->answers = &answer_rec; + bzero((char *)nmb2->answers,sizeof(*nmb2->answers)); + + + nmb2->answers->rr_name = nmb->question.question_name; + nmb2->answers->rr_type = nmb->question.question_type; + nmb2->answers->rr_class = nmb->question.question_class; + nmb2->answers->ttl = 0; + + for (count=0, n = namelist ; n; n = n->next) { + if (n->source != SELF) continue; + count++; + } + + count = MIN(count,400/18); /* XXXX hack, we should calculate exactly + how many will fit */ + + + buf = &nmb2->answers->rdata[0]; + SCVAL(buf,0,count); + buf += 1; + + for (n = namelist ; n; n = n->next) + { + if (n->source != SELF) continue; + + bzero(buf,18); + strcpy(buf,n->name.name); + strupper(buf); + buf[15] = n->name.name_type; + buf += 16; + buf[0] = 0x4; /* active */ + if (!n->unique) buf[0] |= 0x80; /* group */ + buf += 2; + count--; + } + + /* XXXXXXX we should fill in more fields of the statistics structure */ + bzero(buf,64); + { + extern int num_good_sends,num_good_receives; + SIVAL(buf,20,num_good_sends); + SIVAL(buf,24,num_good_receives); + } + SIVAL(buf,46,0xFFB8E5); /* undocumented - used by NT */ + + buf += 64; + + nmb2->answers->rdlength = PTR_DIFF(buf,&nmb2->answers->rdata[0]); + + send_packet(&p2); +} + + + +/**************************************************************************** +reply to a name query +****************************************************************************/ +static void reply_name_query(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char *qname = nmb->question.question_name.name; + BOOL wildcard = (qname[0] == '*'); + BOOL bcast = nmb->header.nm_flags.bcast; + struct in_addr retip; + int name_type = nmb->question.question_name.name_type; + struct packet_struct p2; + struct nmb_packet *nmb2; + struct res_rec answer_rec; + int ttl=0; + int rcode=0; + BOOL unique = True; + + DEBUG(3,("Name query for %s from %s (bcast=%s) - ", + namestr(&nmb->question.question_name), + inet_ntoa(p->ip), + BOOLSTR(bcast))); + + if (wildcard) + retip = myip; + + if (!wildcard) { + struct name_record *n = find_name(&nmb->question.question_name); + + if (!n) { + struct in_addr ip; + unsigned long a; + + /* only do DNS lookups if the query is for type 0x20 or type 0x0 */ + if (name_type != 0x20 && name_type != 0) { + DEBUG(3,("not found\n")); + return; + } + + /* look it up with DNS */ + a = interpret_addr(qname); + + putip((char *)&ip,(char *)&a); + + if (!a) { + /* no luck with DNS. We could possibly recurse here XXXX */ + /* if this isn't a bcast then we should send a negative reply XXXX */ + DEBUG(3,("no recursion\n")); + add_host_entry(qname,name_type,True,60*60,DNSFAIL,ip); + return; + } + + /* add it to our cache of names. give it 2 hours in the cache */ + n = add_host_entry(qname,name_type,True,2*60*60,DNS,ip); + + /* failed to add it? yikes! */ + if (!n) return; + } + + /* don't respond to bcast queries for group names unless we own them */ + if (bcast && !n->unique && !n->source == SELF) { + DEBUG(3,("no bcast replies\n")); + return; + } + + /* don't respond to bcast queries for addresses on the same net as the + machine doing the querying unless its our IP */ + if (bcast && + n->source != SELF && + same_net(n->ip,p->ip)) { + DEBUG(3,("same net\n")); + return; + } + + /* is our entry already dead? */ + if (n->death_time) { + if (n->death_time < p->timestamp) return; + ttl = n->death_time - p->timestamp; + } + + retip = n->ip; + unique = n->unique; + + /* it may have been an earlier failure */ + if (n->source == DNSFAIL) { + DEBUG(3,("DNSFAIL\n")); + return; + } + } + + /* if the IP is 0 then substitute my IP - we should see which one is on the + right interface for the caller to do this right XXX */ + if (zero_ip(retip)) retip = myip; + + DEBUG(3,("OK %s rcode=%d\n",inet_ntoa(retip),rcode)); + + /* a lot of fields get copied from the query. This gives us the IP + and port the reply will be sent to etc */ + p2 = *p; + nmb2 = &p2.packet.nmb; + + nmb2->header.response = True; + nmb2->header.nm_flags.bcast = False; + nmb2->header.nm_flags.recursion_available = CanRecurse; + nmb2->header.nm_flags.trunc = False; + nmb2->header.nm_flags.authoritative = True; /* WfWg ignores + non-authoritative answers */ + nmb2->header.qdcount = 0; + nmb2->header.ancount = 1; + nmb2->header.nscount = 0; + nmb2->header.arcount = 0; + nmb2->header.rcode = rcode; + + nmb2->answers = &answer_rec; + bzero((char *)nmb2->answers,sizeof(*nmb2->answers)); + + nmb2->answers->rr_name = nmb->question.question_name; + nmb2->answers->rr_type = nmb->question.question_type; + nmb2->answers->rr_class = nmb->question.question_class; + nmb2->answers->ttl = ttl; + nmb2->answers->rdlength = 6; + nmb2->answers->rdata[0] = unique?0:0x80; + nmb2->answers->rdata[1] = 0; + putip(&nmb2->answers->rdata[2],(char *)&retip); + + send_packet(&p2); +} + + + +/* the global packet linked-list. incoming entries are added to the + end of this list. it is supposed to remain fairly short so we + won't bother with an end pointer. */ +static struct packet_struct *packet_queue = NULL; + + +/******************************************************************* + queue a packet into the packet queue + ******************************************************************/ +static void queue_packet(struct packet_struct *packet) +{ + struct packet_struct *p; + if (!packet_queue) { + packet->prev = NULL; + packet->next = NULL; + packet_queue = packet; + return; + } + + /* find the bottom */ + for (p=packet_queue;p->next;p=p->next) ; + + p->next = packet; + packet->next = NULL; + packet->prev = p; +} + +/**************************************************************************** + process a nmb packet + ****************************************************************************/ +static void process_nmb(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + + /* if this is a response then ignore it */ + if (nmb->header.response) return; + + switch (nmb->header.opcode) + { + case 5: + case 8: + case 9: + if (nmb->header.qdcount>0 && + nmb->header.arcount>0) { + reply_name_reg(p); + return; + } + break; + + case 0: + if (nmb->header.qdcount>0) + { + switch (nmb->question.question_type) + { + case 0x20: + reply_name_query(p); + break; + + case 0x21: + reply_name_status(p); + break; + } + return; + } + break; + + case 6: + if (nmb->header.qdcount>0 && + nmb->header.arcount>0) { + reply_name_release(p); + return; + } + break; + } + +} + + + +/******************************************************************* + run elements off the packet queue till its empty + ******************************************************************/ +static void run_packet_queue(void) +{ + struct packet_struct *p; + + while ((p=packet_queue)) { + switch (p->packet_type) + { + case NMB_PACKET: + process_nmb(p); + break; + + case DGRAM_PACKET: + process_dgram(p); + break; + } + + packet_queue = packet_queue->next; + if (packet_queue) packet_queue->prev = NULL; + free_packet(p); + } +} + + +/**************************************************************************** + The main select loop, listen for packets and respond + ***************************************************************************/ +void process(void) +{ + + while (True) + { + fd_set fds; + int selrtn; + struct timeval timeout; + + if (needelection && PrimaryGroup[0] && !RunningElection) { + DEBUG(3,(">>> Starting election on %s <<<\n",PrimaryGroup)); + ElectionCount = 0; + RunningElection = True; + needelection = False; + } + + FD_ZERO(&fds); + FD_SET(ClientNMB,&fds); + FD_SET(ClientDGRAM,&fds); + /* during elections we need to send election packets at one + second intervals */ + timeout.tv_sec = RunningElection?1:NMBD_SELECT_LOOP; + timeout.tv_usec = 0; + + selrtn = sys_select(&fds,&timeout); + + if (FD_ISSET(ClientNMB,&fds)) { + struct packet_struct *packet = read_packet(ClientNMB,NMB_PACKET); + if (packet) queue_packet(packet); + } + + if (FD_ISSET(ClientDGRAM,&fds)) { + struct packet_struct *packet = read_packet(ClientDGRAM,DGRAM_PACKET); + if (packet) queue_packet(packet); + } + + if (RunningElection) + run_election(); + + run_packet_queue(); + + do_announcements(); + + housekeeping(); + } +} + + +/**************************************************************************** + open the socket communication +****************************************************************************/ +static BOOL open_sockets(BOOL isdaemon,int port) +{ + struct hostent *hp; + + /* get host info */ + if ((hp = Get_Hostbyname(myhostname)) == 0) + { + DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname)); + return False; + } + + if (isdaemon) + ClientNMB = open_socket_in(SOCK_DGRAM, port,0); + else + ClientNMB = 0; + + ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3); + + if (ClientNMB == -1) + return(False); + + signal(SIGPIPE, SIGNAL_CAST sig_pipe); + + set_socket_options(ClientNMB,"SO_BROADCAST"); + set_socket_options(ClientDGRAM,"SO_BROADCAST"); + + DEBUG(3, ("Socket opened.\n")); + return True; +} + + +/******************************************************************* + check that a IP, bcast and netmask and consistent. Must be a 1s + broadcast + ******************************************************************/ +static BOOL ip_consistent(struct in_addr ip,struct in_addr bcast, + struct in_addr nmask) +{ + unsigned long a_ip,a_bcast,a_nmask; + + a_ip = ntohl(ip.s_addr); + a_bcast = ntohl(bcast.s_addr); + a_nmask = ntohl(nmask.s_addr); + + /* check the netmask is sane */ + if (((a_nmask>>24)&0xFF) != 0xFF) { + DEBUG(0,("Insane netmask %s\n",inet_ntoa(nmask))); + return(False); + } + + /* check the IP and bcast are on the same net */ + if ((a_ip&a_nmask) != (a_bcast&a_nmask)) { + DEBUG(0,("IP and broadcast are on different nets!\n")); + return(False); + } + + /* check the IP and bcast are on the same net */ + if ((a_bcast|a_nmask) != 0xFFFFFFFF) { + DEBUG(0,("Not a ones based broadcast %s\n",inet_ntoa(bcast))); + return(False); + } + + return(True); +} + +/**************************************************************************** + initialise connect, service and file structs +****************************************************************************/ +static BOOL init_structs(void ) +{ + if (!get_myname(myhostname,got_myip?NULL:&myip)) + return(False); + + /* Read the broadcast address from the interface */ + { + struct in_addr ip0,ip1,ip2; + + ip0 = myip; + + if (!(got_bcast && got_nmask)) + { + get_broadcast(&ip0,&ip1,&ip2); + + if (!got_myip) + myip = ip0; + + if (!got_bcast) + bcast_ip = ip1; + + if (!got_nmask) + Netmask = ip2; + } + + DEBUG(1,("Using IP %s ",inet_ntoa(myip))); + DEBUG(1,("broadcast %s ",inet_ntoa(bcast_ip))); + DEBUG(1,("netmask %s\n",inet_ntoa(Netmask))); + + if (!ip_consistent(myip,bcast_ip,Netmask)) { + DEBUG(0,("WARNING: The IP address, broadcast and Netmask are not consistent\n")); + DEBUG(0,("You are likely to experience problems with this setup!\n")); + } + } + + if (! *myname) { + char *p; + strcpy(myname,myhostname); + p = strchr(myname,'.'); + if (p) *p = 0; + } + + { + extern fstring local_machine; + strcpy(local_machine,myname); + strupper(local_machine); + } + + return True; +} + +/**************************************************************************** +usage on the program +****************************************************************************/ +static void usage(char *pname) +{ + DEBUG(0,("Incorrect program usage - is the command line correct?\n")); + + printf("Usage: %s [-n name] [-B bcast address] [-D] [-p port] [-d debuglevel] [-l log basename]\n",pname); + printf("Version %s\n",VERSION); + printf("\t-D become a daemon\n"); + printf("\t-p port listen on the specified port\n"); + printf("\t-d debuglevel set the debuglevel\n"); + printf("\t-l log basename. Basename for log/debug files\n"); + printf("\t-n netbiosname. the netbios name to advertise for this host\n"); + printf("\t-B broadcast address the address to use for broadcasts\n"); + printf("\t-N netmask the netmask to use for subnet determination\n"); + printf("\t-H hosts file load a netbios hosts file\n"); + printf("\t-I ip-address override the IP address\n"); + printf("\t-G group name add a group name to be part of\n"); + printf("\t-C comment sets the machine comment that appears in browse lists\n"); + printf("\n"); +} + + +/**************************************************************************** + main program + **************************************************************************/ +int main(int argc,char *argv[]) +{ + int port = NMB_PORT; + int opt; + extern FILE *dbf; + extern char *optarg; + + *host_file = 0; + +#if 0 + sleep(10); +#endif + + StartupTime = time(NULL); + + TimeInit(); + + strcpy(debugf,NMBLOGFILE); + + setup_logging(argv[0],False); + + charset_initialise(); + +#ifdef LMHOSTSFILE + strcpy(host_file,LMHOSTSFILE); +#endif + + /* this is for people who can't start the program correctly */ + while (argc > 1 && (*argv[1] != '-')) + { + argv++; + argc--; + } + + fault_setup(fault_continue); + + signal(SIGHUP,SIGNAL_CAST sig_hup); + + bcast_ip = *interpret_addr2("0.0.0.0"); + myip = *interpret_addr2("0.0.0.0"); + + while ((opt = getopt (argc, argv, "s:T:I:C:bAi:B:N:Rn:l:d:Dp:hSH:G:")) != EOF) + switch (opt) + { + case 's': + strcpy(servicesf,optarg); + break; + case 'C': + strcpy(ServerComment,optarg); + break; + case 'G': + add_domain_entry(optarg,bcast_ip); + break; + case 'H': + strcpy(host_file,optarg); + break; + case 'I': + myip = *interpret_addr2(optarg); + got_myip = True; + break; + case 'B': + bcast_ip = *interpret_addr2(optarg); + got_bcast = True; + break; + case 'N': + Netmask = *interpret_addr2(optarg); + got_nmask = True; + break; + case 'n': + strcpy(myname,optarg); + break; + case 'l': + sprintf(debugf,"%s.nmb",optarg); + break; + case 'i': + strcpy(scope,optarg); + strupper(scope); + break; + case 'D': + is_daemon = True; + break; + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + default: + if (!is_a_socket(0)) + usage(argv[0]); + break; + } + + DEBUG(1,("%s netbios nameserver version %s started\n",timestring(),VERSION)); + DEBUG(1,("Copyright Andrew Tridgell 1994\n")); + + init_structs(); + + if (!reload_services(False)) + return(-1); + + if (*host_file) + { + load_hosts_file(host_file); + DEBUG(3,("Loaded hosts file\n")); + } + + if (!*ServerComment) + strcpy(ServerComment,"Samba %v"); + string_sub(ServerComment,"%v",VERSION); + string_sub(ServerComment,"%h",myhostname); + + add_my_names(); + + DEBUG(3,("Checked names\n")); + + dump_names(); + + DEBUG(3,("Dumped names\n")); + + if (!is_daemon && !is_a_socket(0)) { + DEBUG(0,("standard input is not a socket, assuming -D option\n")); + is_daemon = True; + } + + + if (is_daemon) { + DEBUG(2,("%s becoming a daemon\n",timestring())); + become_daemon(); + } + + + DEBUG(3,("Opening sockets\n")); + + if (open_sockets(is_daemon,port)) + { + process(); + close_sockets(); + } + + if (dbf) + fclose(dbf); + return(0); +} diff --git a/source3/nmbsync.c b/source3/nmbsync.c new file mode 100644 index 0000000000..5a77d6cc48 --- /dev/null +++ b/source3/nmbsync.c @@ -0,0 +1,303 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT netbios routines to synchronise browse lists + Copyright (C) Andrew Tridgell 1994-1995 + + 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. + +*/ + +#include "includes.h" +#include "loadparm.h" +#include "nameserv.h" + +extern int DEBUGLEVEL; + +struct server_record *add_server_entry(char *name,int servertype, + int ttl,char *comment,BOOL replace); + + +/**************************************************************************** +call a remote api +****************************************************************************/ +static BOOL call_remote_api(int fd,int cnum,int uid,int timeout, + char *inbuf,char *outbuf, + int prcnt,int drcnt, + int mprcnt,int mdrcnt, + int *rprcnt,int *rdrcnt, + char *param,char *data, + char **rparam,char **rdata) +{ + char *p1,*p2; + + /* send a SMBtrans command */ + bzero(outbuf,smb_size); + set_message(outbuf,14,0,True); + CVAL(outbuf,smb_com) = SMBtrans; + SSVAL(outbuf,smb_tid,cnum); + SSVAL(outbuf,smb_uid,uid); + + p1 = smb_buf(outbuf); + strcpy(p1,"\\PIPE\\LANMAN"); + p1 = skip_string(p1,1); + p2 = p1 + prcnt; + + if (prcnt > 0) + memcpy(p1,param,prcnt); + if (drcnt > 0) + memcpy(p2,data,drcnt); + + SSVAL(outbuf,smb_vwv0,prcnt); /* param count */ + SSVAL(outbuf,smb_vwv1,drcnt); /* data count */ + SSVAL(outbuf,smb_vwv2,mprcnt); /* mprcnt */ + SSVAL(outbuf,smb_vwv3,mdrcnt); /* mdrcnt */ + SSVAL(outbuf,smb_vwv4,0); /* msrcnt */ + SSVAL(outbuf,smb_vwv5,0); /* flags */ + SSVAL(outbuf,smb_vwv9,prcnt); /* pscnt */ + SSVAL(outbuf,smb_vwv10,smb_offset(p1,outbuf)); /* psoff */ + SSVAL(outbuf,smb_vwv11,drcnt); /* dscnt */ + SSVAL(outbuf,smb_vwv12,smb_offset(p2,outbuf)); /* dsoff */ + CVAL(outbuf,smb_vwv13) = 0; /* suwcnt */ + + set_message(outbuf,14,PTR_DIFF(p2+drcnt,smb_buf(outbuf)),False); + + send_smb(fd,outbuf); + + if (receive_smb(fd,inbuf,timeout) && + CVAL(inbuf,smb_rcls) == 0) + { + if (rparam) + *rparam = inbuf+4 + SVAL(inbuf,smb_vwv4); + if (rdata) + *rdata = inbuf+4 + SVAL(inbuf,smb_vwv7); + if (rprcnt) + *rprcnt = SVAL(inbuf,smb_vwv3); + if (rdrcnt) + *rdrcnt = SVAL(inbuf,smb_vwv6); + return(True); + } + + return(False); +} + + +/******************************************************************* + synchronise browse lists with another browse server + ******************************************************************/ +void sync_browse_lists(char *name,int name_type,char *myname, + char *domain,struct in_addr ip) +{ + char *protocol = "LM1.2X002"; + char *service = "IPC$"; + char *dev = "IPC"; + int timeout=2000; + char *inbuf=NULL; + pstring outbuf; + char *p; + int len; + uint32 sesskey; + int cnum,uid; + BOOL ret; + + int fd = open_socket_out(SOCK_STREAM, &ip, SMB_PORT); + if (fd < 0) { + DEBUG(3,("Failed to connect to %s at %s\n",name,inet_ntoa(ip))); + return; + } + + if (!(inbuf = (char *)malloc(0xFFFF+1024))) return; + + /* put in the destination name */ + len = 4; + p = outbuf+len; + name_mangle(name,p,name_type); + len += name_len(p); + + /* and my name */ + p = outbuf+len; + name_mangle(myname,p,0x20); + len += name_len(p); + + _smb_setlen(outbuf,len); + CVAL(outbuf,0) = 0x81; + + send_smb(fd,outbuf); + receive_smb(fd,inbuf,5000); + + bzero(outbuf,smb_size); + + /* setup the protocol string */ + set_message(outbuf,0,strlen(protocol)+2,True); + p = smb_buf(outbuf); + *p++ = 2; + strcpy(p,protocol); + + CVAL(outbuf,smb_com) = SMBnegprot; + CVAL(outbuf,smb_flg) = 0x8; + SSVAL(outbuf,smb_flg2,0x1); + + send_smb(fd,outbuf); + bzero(inbuf,smb_size); + ret = receive_smb(fd,inbuf,timeout); + + if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) { + DEBUG(3,("%s rejected the protocol\n",name)); + close(fd); + if (inbuf) free(inbuf); + return; + } + + sesskey = IVAL(inbuf,smb_vwv6); + + bzero(outbuf,smb_size); + set_message(outbuf,10,2,True); + CVAL(outbuf,smb_com) = SMBsesssetupX; + + CVAL(outbuf,smb_vwv0) = 0xFF; + SSVAL(outbuf,smb_vwv2,0xFFFF); + SSVAL(outbuf,smb_vwv3,2); + SSVAL(outbuf,smb_vwv4,1); + SIVAL(outbuf,smb_vwv5,sesskey); + SSVAL(outbuf,smb_vwv7,1); + + send_smb(fd,outbuf); + bzero(inbuf,smb_size); + ret = receive_smb(fd,inbuf,timeout); + if (!ret || CVAL(inbuf,smb_rcls)) { + DEBUG(3,("%s rejected session setup\n",name)); + close(fd); + if (inbuf) free(inbuf); + return; + } + + uid = SVAL(inbuf,smb_uid); + + bzero(outbuf,smb_size); + set_message(outbuf,4,2 + (2 + strlen(name) + 1 + strlen(service)) + + 1 + strlen(dev),True); + CVAL(outbuf,smb_com) = SMBtconX; + SSVAL(outbuf,smb_uid,uid); + + SSVAL(outbuf,smb_vwv0,0xFF); + SSVAL(outbuf,smb_vwv3,1); + + p = smb_buf(outbuf) + 1; + strcpy(p, "\\\\"); + strcat(p, name); + strcat(p, "\\"); + strcat(p,service); + p = skip_string(p,1); + strcpy(p,dev); + + send_smb(fd,outbuf); + bzero(inbuf,smb_size); + ret = receive_smb(fd,inbuf,timeout); + if (!ret || CVAL(inbuf,smb_rcls)) { + DEBUG(3,("%s rejected IPC connect (%d,%d)\n",name, + CVAL(inbuf,smb_rcls),SVAL(inbuf,smb_err))); + close(fd); + if (inbuf) free(inbuf); + return; + } + + cnum = SVAL(inbuf,smb_tid); + + /* now I need to send a NetServerEnum */ + { + fstring param; + uint32 *typep; + char *rparam,*rdata; + + p = param; + SSVAL(p,0,0x68); /* api number */ + p += 2; + strcpy(p,"WrLehDz"); + p = skip_string(p,1); + + strcpy(p,"B16BBDz"); + + p = skip_string(p,1); + SSVAL(p,0,1); /* level 1 */ + SSVAL(p,2,0xFFFF - 500); /* buf length */ + p += 4; + typep = (uint32 *)p; + p += 4; + strcpy(p,domain); + strupper(p); + p = skip_string(p,1); + + SIVAL(typep,0,0x80000000); /* domain list */ + + if (call_remote_api(fd,cnum,uid,timeout,inbuf,outbuf, + PTR_DIFF(p,param),0, + 8,0xFFFF - 500, + NULL,NULL, + param,NULL, + &rparam,&rdata) && SVAL(rparam,0)==0) + { + int converter=SVAL(rparam,2); + int count=SVAL(rparam,4); + int i; + char *p2 = rdata; + for (i=0;i<count;i++) { + char *sname = p2; + uint32 type = IVAL(p2,18); + int comment_offset = IVAL(p2,22) & 0xFFFF; + char *comment = comment_offset?(rdata+comment_offset-converter):""; + + add_server_entry(sname,type,lp_max_ttl(),comment,False); + p2 += 26; + } + } + + SIVAL(typep,0,0xFFFFFFFF); /* server list */ + + if (call_remote_api(fd,cnum,uid,timeout,inbuf,outbuf, + PTR_DIFF(p,param),0, + 8,0xFFFF - 500, + NULL,NULL, + param,NULL, + &rparam,&rdata) && SVAL(rparam,0)==0) + { + int converter=SVAL(rparam,2); + int count=SVAL(rparam,4); + int i; + + p = rdata; + for (i=0;i<count;i++) { + char *sname = p; + uint32 type = IVAL(p,18); + int comment_offset = IVAL(p,22) & 0xFFFF; + char *comment = comment_offset?(rdata+comment_offset-converter):""; + + add_server_entry(sname,type,lp_max_ttl(),comment,False); + p += 26; + } + } + } + + /* close up */ + bzero(outbuf,smb_size); + set_message(outbuf,0,0,True); + CVAL(outbuf,smb_com) = SMBtdis; + SSVAL(outbuf,smb_uid,uid); + SSVAL(outbuf,smb_tid,cnum); + send_smb(fd,outbuf); + receive_smb(fd,inbuf,1000); + + close(fd); + if (inbuf) free(inbuf); +} diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c new file mode 100644 index 0000000000..c61ab26781 --- /dev/null +++ b/source3/param/loadparm.c @@ -0,0 +1,1891 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Parameter loading functions + Copyright (C) Karl Auer 1993,1994 + + Largely re-written by Andrew Tridgell, September 1994 + + 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. +*/ + +/* + * Load parameters. + * + * This module provides suitable callback functions for the params + * module. It builds the internal table of service details which is + * then used by the rest of the server. + * + * To add a parameter: + * + * 1) add it to the global or service structure definition + * 2) add it to the parm_table + * 3) add it to the list of available functions (eg: using FN_GLOBAL_STRING()) + * 4) If it's a global then initialise it in init_globals. If a local + * (ie. service) parameter then initialise it in the sDefault structure + * + * + * Notes: + * The configuration file is processed sequentially for speed. It is NOT + * accessed randomly as happens in 'real' Windows. For this reason, there + * is a fair bit of sequence-dependent code here - ie., code which assumes + * that certain things happen before others. In particular, the code which + * happens at the boundary between sections is delicately poised, so be + * careful! + * + */ + +#include "includes.h" + +#include "params.h" +#include "loadparm.h" +#include "pcap.h" + +BOOL bLoaded = False; + +extern int DEBUGLEVEL; +extern int ReadSize; +extern pstring user_socket_options; +extern pstring smbrun_path; + +#ifndef GLOBAL_NAME +#define GLOBAL_NAME "global" +#endif + +#ifndef PRINTCAP_NAME +#ifdef AIX +#define PRINTCAP_NAME "/etc/qconfig" +#else +#define PRINTCAP_NAME "/etc/printcap" +#endif +#endif + +#ifndef PRINTERS_NAME +#define PRINTERS_NAME "printers" +#endif + +#ifndef HOMES_NAME +#define HOMES_NAME "homes" +#endif + +/* some helpful bits */ +#define pSERVICE(i) ServicePtrs[i] +#define iSERVICE(i) (*pSERVICE(i)) +#define LP_SNUM_OK(iService) (((iService) >= 0) && ((iService) < iNumServices) && iSERVICE(iService).valid) +#define VALID(i) iSERVICE(i).valid + +/* these are the types of parameter we have */ +typedef enum +{ + P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_STRING,P_GSTRING +} parm_type; + +typedef enum +{ + P_LOCAL,P_GLOBAL,P_NONE +} parm_class; + +int keepalive=0; +extern BOOL use_getwd_cache; + +extern int extra_time_offset; +#ifdef KANJI +extern int coding_system; +#endif + +/* + * This structure describes global (ie., server-wide) parameters. + */ +typedef struct +{ + char *szPrintcapname; + char *szLockDir; + char *szRootdir; + char *szDefaultService; + char *szDfree; + char *szMsgCommand; + char *szHostsEquiv; + char *szServerString; + char *szAutoServices; + char *szPasswdProgram; + char *szPasswdChat; + char *szLogFile; + char *szConfigFile; + char *szSMBPasswdFile; + char *szPasswordServer; + char *szSocketOptions; + char *szValidChars; + char *szWorkGroup; + char *szDomainController; + char *szUsernameMap; + char *szCharacterSet; + char *szLogonScript; + int max_log_size; + int mangled_stack; + int max_xmit; + int max_mux; + int max_packet; + int pwordlevel; + int deadtime; + int maxprotocol; + int security; + int printing; + int maxdisksize; + int lpqcachetime; + int syslog; + int os_level; + int max_ttl; + BOOL bPreferredMaster; + BOOL bDomainMaster; + BOOL bDomainLogons; + BOOL bEncryptPasswords; + BOOL bStripDot; + BOOL bNullPasswords; + BOOL bLoadPrinters; + BOOL bUseRhosts; + BOOL bReadRaw; + BOOL bWriteRaw; + BOOL bReadPrediction; + BOOL bReadbmpx; + BOOL bSyslogOnly; + BOOL bBrowseList; +} global; + +static global Globals; + + + +/* + * This structure describes a single service. + */ +typedef struct +{ + BOOL valid; + char *szService; + char *szPath; + char *szUsername; + char *szGuestaccount; + char *szInvalidUsers; + char *szValidUsers; + char *szAdminUsers; + char *szCopy; + char *szInclude; + char *szPreExec; + char *szPostExec; + char *szRootPreExec; + char *szRootPostExec; + char *szPrintcommand; + char *szLpqcommand; + char *szLprmcommand; + char *szLppausecommand; + char *szLpresumecommand; + char *szPrintername; + char *szDontdescend; + char *szHostsallow; + char *szHostsdeny; + char *szMagicScript; + char *szMagicOutput; + char *szMangledMap; + char *comment; + char *force_user; + char *force_group; + char *readlist; + char *writelist; + char *volume; + int iMinPrintSpace; + int iCreate_mode; + int iMaxConnections; + int iDefaultCase; + BOOL bAlternatePerm; + BOOL bRevalidate; + BOOL bCaseSensitive; + BOOL bCasePreserve; + BOOL bShortCasePreserve; + BOOL bCaseMangle; + BOOL status; + BOOL bHideDotFiles; + BOOL bBrowseable; + BOOL bAvailable; + BOOL bRead_only; + BOOL bNo_set_dir; + BOOL bGuest_only; + BOOL bGuest_ok; + BOOL bPrint_ok; + BOOL bPostscript; + BOOL bMap_system; + BOOL bMap_hidden; + BOOL bMap_archive; + BOOL bLocking; + BOOL bStrictLocking; + BOOL bShareModes; + BOOL bOnlyUser; + BOOL bMangledNames; + BOOL bWidelinks; + BOOL bSyncAlways; + char magic_char; + BOOL *copymap; + char dummy[3]; /* for alignment */ +} service; + + +/* This is a default service used to prime a services structure */ +static service sDefault = +{ + True, /* valid */ + NULL, /* szService */ + NULL, /* szPath */ + NULL, /* szUsername */ + NULL, /* szGuestAccount */ + NULL, /* szInvalidUsers */ + NULL, /* szValidUsers */ + NULL, /* szAdminUsers */ + NULL, /* szCopy */ + NULL, /* szInclude */ + NULL, /* szPreExec */ + NULL, /* szPostExec */ + NULL, /* szRootPreExec */ + NULL, /* szRootPostExec */ + NULL, /* szPrintcommand */ + NULL, /* szLpqcommand */ + NULL, /* szLprmcommand */ + NULL, /* szLppausecommand */ + NULL, /* szLpresumecommand */ + NULL, /* szPrintername */ + NULL, /* szDontdescend */ + NULL, /* szHostsallow */ + NULL, /* szHostsdeny */ + NULL, /* szMagicScript */ + NULL, /* szMagicOutput */ + NULL, /* szMangledMap */ + NULL, /* comment */ + NULL, /* force user */ + NULL, /* force group */ + NULL, /* readlist */ + NULL, /* writelist */ + NULL, /* volume */ + 0, /* iMinPrintSpace */ + 0755, /* iCreate_mode */ + 0, /* iMaxConnections */ + CASE_LOWER, /* iDefaultCase */ + False, /* bAlternatePerm */ + False, /* revalidate */ + False, /* case sensitive */ + False, /* case preserve */ + False, /* short case preserve */ + False, /* case mangle */ + True, /* status */ + True, /* bHideDotFiles */ + True, /* bBrowseable */ + True, /* bAvailable */ + True, /* bRead_only */ + True, /* bNo_set_dir */ + False, /* bGuest_only */ + False, /* bGuest_ok */ + False, /* bPrint_ok */ + False, /* bPostscript */ + False, /* bMap_system */ + False, /* bMap_hidden */ + True, /* bMap_archive */ + True, /* bLocking */ + False, /* bStrictLocking */ + True, /* bShareModes */ + False, /* bOnlyUser */ + True, /* bMangledNames */ + True, /* bWidelinks */ + False, /* bSyncAlways */ + '~', /* magic char */ + NULL, /* copymap */ + "" /* dummy */ +}; + + + +/* local variables */ +static service **ServicePtrs = NULL; +static int iNumServices = 0; +static int iServiceIndex = 0; +static BOOL bInGlobalSection = True; +static BOOL bGlobalOnly = False; + + +#define NUMPARAMETERS (sizeof(parm_table) / sizeof(struct parm_struct)) + +/* prototypes for the special type handlers */ +static BOOL handle_valid_chars(char *pszParmValue, char **ptr); +static BOOL handle_include(char *pszParmValue, char **ptr); +static BOOL handle_copy(char *pszParmValue, char **ptr); +static BOOL handle_protocol(char *pszParmValue,int *val); +static BOOL handle_security(char *pszParmValue,int *val); +static BOOL handle_case(char *pszParmValue,int *val); +static BOOL handle_printing(char *pszParmValue,int *val); +static BOOL handle_character_set(char *pszParmValue,int *val); +#ifdef KANJI +static BOOL handle_coding_system(char *pszParmValue,int *val); +#endif /* KANJI */ + +struct parm_struct +{ + char *label; + parm_type type; + parm_class class; + void *ptr; + BOOL (*special)(); +} parm_table[] = +{ + {"debuglevel", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL}, + {"log level", P_INTEGER, P_GLOBAL, &DEBUGLEVEL, NULL}, + {"syslog", P_INTEGER, P_GLOBAL, &Globals.syslog, NULL}, + {"syslog only", P_BOOL, P_GLOBAL, &Globals.bSyslogOnly, NULL}, + {"protocol", P_INTEGER, P_GLOBAL, &Globals.maxprotocol,handle_protocol}, + {"security", P_INTEGER, P_GLOBAL, &Globals.security,handle_security}, + {"printing", P_INTEGER, P_GLOBAL, &Globals.printing,handle_printing}, + {"max disk size", P_INTEGER, P_GLOBAL, &Globals.maxdisksize, NULL}, + {"lpq cache time", P_INTEGER, P_GLOBAL, &Globals.lpqcachetime, NULL}, + {"encrypt passwords",P_BOOL, P_GLOBAL, &Globals.bEncryptPasswords, NULL}, + {"getwd cache", P_BOOL, P_GLOBAL, &use_getwd_cache, NULL}, + {"read prediction", P_BOOL, P_GLOBAL, &Globals.bReadPrediction, NULL}, + {"read bmpx", P_BOOL, P_GLOBAL, &Globals.bReadbmpx, NULL}, + {"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL}, + {"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL}, + {"use rhosts", P_BOOL, P_GLOBAL, &Globals.bUseRhosts, NULL}, + {"load printers", P_BOOL, P_GLOBAL, &Globals.bLoadPrinters, NULL}, + {"null passwords", P_BOOL, P_GLOBAL, &Globals.bNullPasswords, NULL}, + {"strip dot", P_BOOL, P_GLOBAL, &Globals.bStripDot, NULL}, + {"password server", P_STRING, P_GLOBAL, &Globals.szPasswordServer, NULL}, + {"socket options", P_GSTRING, P_GLOBAL, user_socket_options, NULL}, + {"smbrun", P_GSTRING, P_GLOBAL, smbrun_path, NULL}, + {"log file", P_STRING, P_GLOBAL, &Globals.szLogFile, NULL}, + {"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL}, + {"smb passwd file", P_STRING, P_GLOBAL, &Globals.szSMBPasswdFile, NULL}, + {"hosts equiv", P_STRING, P_GLOBAL, &Globals.szHostsEquiv, NULL}, + {"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL}, + {"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL}, + {"server string", P_STRING, P_GLOBAL, &Globals.szServerString, NULL}, + {"printcap name", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL}, + {"printcap", P_STRING, P_GLOBAL, &Globals.szPrintcapname, NULL}, + {"lock dir", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL}, + {"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL}, + {"root directory", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL}, + {"root dir", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL}, + {"root", P_STRING, P_GLOBAL, &Globals.szRootdir, NULL}, + {"default service", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL}, + {"default", P_STRING, P_GLOBAL, &Globals.szDefaultService, NULL}, + {"message command", P_STRING, P_GLOBAL, &Globals.szMsgCommand, NULL}, + {"dfree command", P_STRING, P_GLOBAL, &Globals.szDfree, NULL}, + {"passwd program", P_STRING, P_GLOBAL, &Globals.szPasswdProgram, NULL}, + {"passwd chat", P_STRING, P_GLOBAL, &Globals.szPasswdChat, NULL}, + {"valid chars", P_STRING, P_GLOBAL, &Globals.szValidChars, handle_valid_chars}, + {"workgroup", P_STRING, P_GLOBAL, &Globals.szWorkGroup, NULL}, + {"domain controller",P_STRING, P_GLOBAL, &Globals.szDomainController,NULL}, + {"username map", P_STRING, P_GLOBAL, &Globals.szUsernameMap, NULL}, + {"character set", P_STRING, P_GLOBAL, &Globals.szCharacterSet, handle_character_set}, + {"logon script", P_STRING, P_GLOBAL, &Globals.szLogonScript, NULL}, + {"max log size", P_INTEGER, P_GLOBAL, &Globals.max_log_size, NULL}, + {"mangled stack", P_INTEGER, P_GLOBAL, &Globals.mangled_stack, NULL}, + {"max mux", P_INTEGER, P_GLOBAL, &Globals.max_mux, NULL}, + {"max xmit", P_INTEGER, P_GLOBAL, &Globals.max_xmit, NULL}, + {"max packet", P_INTEGER, P_GLOBAL, &Globals.max_packet, NULL}, + {"packet size", P_INTEGER, P_GLOBAL, &Globals.max_packet, NULL}, + {"password level", P_INTEGER, P_GLOBAL, &Globals.pwordlevel, NULL}, + {"keepalive", P_INTEGER, P_GLOBAL, &keepalive, NULL}, + {"deadtime", P_INTEGER, P_GLOBAL, &Globals.deadtime, NULL}, + {"time offset", P_INTEGER, P_GLOBAL, &extra_time_offset, NULL}, + {"read size", P_INTEGER, P_GLOBAL, &ReadSize, NULL}, +#ifdef KANJI + {"coding system", P_INTEGER, P_GLOBAL, &coding_system, handle_coding_system}, +#endif /* KANJI */ + {"os level", P_INTEGER, P_GLOBAL, &Globals.os_level, NULL}, + {"max ttl", P_INTEGER, P_GLOBAL, &Globals.max_ttl, NULL}, + {"preferred master", P_BOOL, P_GLOBAL, &Globals.bPreferredMaster, NULL}, + {"prefered master", P_BOOL, P_GLOBAL, &Globals.bPreferredMaster, NULL}, + {"domain master", P_BOOL, P_GLOBAL, &Globals.bDomainMaster, NULL}, + {"domain logons", P_BOOL, P_GLOBAL, &Globals.bDomainLogons, NULL}, + {"browse list", P_BOOL, P_GLOBAL, &Globals.bBrowseList, NULL}, + + {"-valid", P_BOOL, P_LOCAL, &sDefault.valid, NULL}, + {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL}, + {"copy", P_STRING, P_LOCAL, &sDefault.szCopy, handle_copy}, + {"include", P_STRING, P_LOCAL, &sDefault.szInclude, handle_include}, + {"exec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL}, + {"preexec", P_STRING, P_LOCAL, &sDefault.szPreExec, NULL}, + {"postexec", P_STRING, P_LOCAL, &sDefault.szPostExec, NULL}, + {"root preexec", P_STRING, P_LOCAL, &sDefault.szRootPreExec, NULL}, + {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL}, + {"alternate permissions",P_BOOL,P_LOCAL, &sDefault.bAlternatePerm, NULL}, + {"revalidate", P_BOOL, P_LOCAL, &sDefault.bRevalidate, NULL}, + {"default case", P_INTEGER, P_LOCAL, &sDefault.iDefaultCase, handle_case}, + {"case sensitive", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL}, + {"casesignames", P_BOOL, P_LOCAL, &sDefault.bCaseSensitive, NULL}, + {"preserve case", P_BOOL, P_LOCAL, &sDefault.bCasePreserve, NULL}, + {"short preserve case",P_BOOL, P_LOCAL, &sDefault.bShortCasePreserve,NULL}, + {"mangle case", P_BOOL, P_LOCAL, &sDefault.bCaseMangle, NULL}, + {"mangling char", P_CHAR, P_LOCAL, &sDefault.magic_char, NULL}, + {"browseable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL}, + {"browsable", P_BOOL, P_LOCAL, &sDefault.bBrowseable, NULL}, + {"available", P_BOOL, P_LOCAL, &sDefault.bAvailable, NULL}, + {"path", P_STRING, P_LOCAL, &sDefault.szPath, NULL}, + {"directory", P_STRING, P_LOCAL, &sDefault.szPath, NULL}, + {"username", P_STRING, P_LOCAL, &sDefault.szUsername, NULL}, + {"user", P_STRING, P_LOCAL, &sDefault.szUsername, NULL}, + {"users", P_STRING, P_LOCAL, &sDefault.szUsername, NULL}, + {"guest account", P_STRING, P_LOCAL, &sDefault.szGuestaccount, NULL}, + {"invalid users", P_STRING, P_LOCAL, &sDefault.szInvalidUsers, NULL}, + {"valid users", P_STRING, P_LOCAL, &sDefault.szValidUsers, NULL}, + {"admin users", P_STRING, P_LOCAL, &sDefault.szAdminUsers, NULL}, + {"read list", P_STRING, P_LOCAL, &sDefault.readlist, NULL}, + {"write list", P_STRING, P_LOCAL, &sDefault.writelist, NULL}, + {"volume", P_STRING, P_LOCAL, &sDefault.volume, NULL}, + {"force user", P_STRING, P_LOCAL, &sDefault.force_user, NULL}, + {"force group", P_STRING, P_LOCAL, &sDefault.force_group, NULL}, + {"group", P_STRING, P_LOCAL, &sDefault.force_group, NULL}, + {"read only", P_BOOL, P_LOCAL, &sDefault.bRead_only, NULL}, + {"write ok", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL}, + {"writeable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL}, + {"writable", P_BOOLREV, P_LOCAL, &sDefault.bRead_only, NULL}, + {"max connections", P_INTEGER, P_LOCAL, &sDefault.iMaxConnections, NULL}, + {"min print space", P_INTEGER, P_LOCAL, &sDefault.iMinPrintSpace, NULL}, + {"create mask", P_OCTAL, P_LOCAL, &sDefault.iCreate_mode, NULL}, + {"create mode", P_OCTAL, P_LOCAL, &sDefault.iCreate_mode, NULL}, + {"set directory", P_BOOLREV, P_LOCAL, &sDefault.bNo_set_dir, NULL}, + {"status", P_BOOL, P_LOCAL, &sDefault.status, NULL}, + {"hide dot files", P_BOOL, P_LOCAL, &sDefault.bHideDotFiles, NULL}, + {"guest only", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL}, + {"only guest", P_BOOL, P_LOCAL, &sDefault.bGuest_only, NULL}, + {"guest ok", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL}, + {"public", P_BOOL, P_LOCAL, &sDefault.bGuest_ok, NULL}, + {"print ok", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL}, + {"printable", P_BOOL, P_LOCAL, &sDefault.bPrint_ok, NULL}, + {"postscript", P_BOOL, P_LOCAL, &sDefault.bPostscript, NULL}, + {"map system", P_BOOL, P_LOCAL, &sDefault.bMap_system, NULL}, + {"map hidden", P_BOOL, P_LOCAL, &sDefault.bMap_hidden, NULL}, + {"map archive", P_BOOL, P_LOCAL, &sDefault.bMap_archive, NULL}, + {"locking", P_BOOL, P_LOCAL, &sDefault.bLocking, NULL}, + {"strict locking", P_BOOL, P_LOCAL, &sDefault.bStrictLocking, NULL}, + {"share modes", P_BOOL, P_LOCAL, &sDefault.bShareModes, NULL}, + {"only user", P_BOOL, P_LOCAL, &sDefault.bOnlyUser, NULL}, + {"wide links", P_BOOL, P_LOCAL, &sDefault.bWidelinks, NULL}, + {"sync always", P_BOOL, P_LOCAL, &sDefault.bSyncAlways, NULL}, + {"mangled names", P_BOOL, P_LOCAL, &sDefault.bMangledNames, NULL}, + {"print command", P_STRING, P_LOCAL, &sDefault.szPrintcommand, NULL}, + {"lpq command", P_STRING, P_LOCAL, &sDefault.szLpqcommand, NULL}, + {"lprm command", P_STRING, P_LOCAL, &sDefault.szLprmcommand, NULL}, + {"lppause command", P_STRING, P_LOCAL, &sDefault.szLppausecommand, NULL}, + {"lpresume command", P_STRING, P_LOCAL, &sDefault.szLpresumecommand,NULL}, + {"printer", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL}, + {"printer name", P_STRING, P_LOCAL, &sDefault.szPrintername, NULL}, + {"hosts allow", P_STRING, P_LOCAL, &sDefault.szHostsallow, NULL}, + {"allow hosts", P_STRING, P_LOCAL, &sDefault.szHostsallow, NULL}, + {"hosts deny", P_STRING, P_LOCAL, &sDefault.szHostsdeny, NULL}, + {"deny hosts", P_STRING, P_LOCAL, &sDefault.szHostsdeny, NULL}, + {"dont descend", P_STRING, P_LOCAL, &sDefault.szDontdescend, NULL}, + {"magic script", P_STRING, P_LOCAL, &sDefault.szMagicScript, NULL}, + {"magic output", P_STRING, P_LOCAL, &sDefault.szMagicOutput, NULL}, + {"mangled map", P_STRING, P_LOCAL, &sDefault.szMangledMap, NULL}, + + {NULL, P_BOOL, P_NONE, NULL, NULL} +}; + + + +/*************************************************************************** +Initialise the global parameter structure. +***************************************************************************/ +static void init_globals(void) +{ + static BOOL done_init = False; + pstring s; + + if (!done_init) + { + int i; + bzero((void *)&Globals,sizeof(Globals)); + + for (i = 0; parm_table[i].label; i++) + if (parm_table[i].type == P_STRING && + parm_table[i].ptr) + string_init(parm_table[i].ptr,""); + + string_set(&sDefault.szGuestaccount, GUEST_ACCOUNT); + + done_init = True; + } + + + DEBUG(3,("Initialising global parameters\n")); + +#ifdef SMB_PASSWD_FILE + string_set(&Globals.szSMBPasswdFile, SMB_PASSWD_FILE); +#endif + string_set(&Globals.szPasswdChat,"*old*password* %o\\n *new*password* %n\\n *new*password* %n\\n *changed*"); + string_set(&Globals.szWorkGroup, WORKGROUP); +#ifdef SMB_PASSWD + string_set(&Globals.szPasswdProgram, SMB_PASSWD); +#else + string_set(&Globals.szPasswdProgram, "/bin/passwd"); +#endif + string_set(&Globals.szPrintcapname, PRINTCAP_NAME); + string_set(&Globals.szLockDir, LOCKDIR); + string_set(&Globals.szRootdir, "/"); + sprintf(s,"Samba %s",VERSION); + string_set(&Globals.szServerString,s); + Globals.bLoadPrinters = True; + Globals.bUseRhosts = False; + Globals.max_packet = 65535; + Globals.mangled_stack = 50; + Globals.max_xmit = Globals.max_packet; + Globals.max_mux = 2; + Globals.lpqcachetime = 10; + Globals.pwordlevel = 0; + Globals.deadtime = 0; + Globals.max_log_size = 5000; + Globals.maxprotocol = PROTOCOL_NT1; + Globals.security = SEC_SHARE; + Globals.bEncryptPasswords = False; + Globals.printing = DEFAULT_PRINTING; + Globals.bReadRaw = True; + Globals.bWriteRaw = True; + Globals.bReadPrediction = False; + Globals.bReadbmpx = True; + Globals.bNullPasswords = False; + Globals.bStripDot = False; + Globals.syslog = 1; + Globals.bSyslogOnly = False; + Globals.os_level = 0; + Globals.max_ttl = 60*60*4; /* 2 hours default */ + Globals.bPreferredMaster = True; + Globals.bDomainMaster = False; + Globals.bDomainLogons = False; + Globals.bBrowseList = True; + +#ifdef KANJI + coding_system = interpret_coding_system (KANJI, SJIS_CODE); +#endif /* KANJI */ + +} + +/*************************************************************************** +check if a string is initialised and if not then initialise it +***************************************************************************/ +static void string_initial(char **s,char *v) +{ + if (!*s || !**s) + string_init(s,v); +} + + +/*************************************************************************** +Initialise the sDefault parameter structure. +***************************************************************************/ +static void init_locals(void) +{ + /* choose defaults depending on the type of printing */ + switch (Globals.printing) + { + case PRINT_BSD: + case PRINT_AIX: + string_initial(&sDefault.szLpqcommand,"lpq -P%p"); + string_initial(&sDefault.szLprmcommand,"lprm -P%p %j"); + string_initial(&sDefault.szPrintcommand,"lpr -r -P%p %s"); + break; + + case PRINT_SYSV: + case PRINT_HPUX: + string_initial(&sDefault.szLpqcommand,"lpstat -o%p"); + string_initial(&sDefault.szLprmcommand,"cancel %p-%j"); + string_initial(&sDefault.szPrintcommand,"lp -c -d%p %s; rm %s"); +#ifdef SVR4 + string_initial(&sDefault.szLppausecommand,"lp -i %p-%j -H hold"); + string_initial(&sDefault.szLpresumecommand,"lp -i %p-%j -H resume"); +#endif + break; + + case PRINT_QNX: + string_initial(&sDefault.szLpqcommand,"lpq -P%p"); + string_initial(&sDefault.szLprmcommand,"lprm -P%p %j"); + string_initial(&sDefault.szPrintcommand,"lp -r -P%p %s"); + break; + + + } +} + + +/******************************************************************* +a convenience rooutine to grab string parameters into a rotating +static buffer, and run standard_sub_basic on them. The buffers +can be written to by callers +********************************************************************/ +char *lp_string(char *s) +{ + static pstring bufs[10]; + static int next=0; + char *ret; + + ret = &bufs[next][0]; + next = (next+1)%10; + + if (!s) + *ret = 0; + else + StrnCpy(ret,s,sizeof(pstring)-1); + + standard_sub_basic(ret); + return(ret); +} + + +/* + In this section all the functions that are used to access the + parameters from the rest of the program are defined +*/ + +#define FN_GLOBAL_STRING(fn_name,ptr) \ + char *fn_name(void) {return(lp_string(*(char **)(ptr) ? *(char **)(ptr) : ""));} +#define FN_GLOBAL_BOOL(fn_name,ptr) \ + BOOL fn_name(void) {return(*(BOOL *)(ptr));} +#define FN_GLOBAL_CHAR(fn_name,ptr) \ + char fn_name(void) {return(*(char *)(ptr));} +#define FN_GLOBAL_INTEGER(fn_name,ptr) \ + int fn_name(void) {return(*(int *)(ptr));} + +#define FN_LOCAL_STRING(fn_name,val) \ + char *fn_name(int i) {return(lp_string((LP_SNUM_OK(i)&&pSERVICE(i)->val)?pSERVICE(i)->val : sDefault.val));} +#define FN_LOCAL_BOOL(fn_name,val) \ + BOOL fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} +#define FN_LOCAL_CHAR(fn_name,val) \ + char fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} +#define FN_LOCAL_INTEGER(fn_name,val) \ + int fn_name(int i) {return(LP_SNUM_OK(i)? pSERVICE(i)->val : sDefault.val);} + +FN_GLOBAL_STRING(lp_logfile,&Globals.szLogFile) +FN_GLOBAL_STRING(lp_configfile,&Globals.szConfigFile) +FN_GLOBAL_STRING(lp_smb_passwd_file,&Globals.szSMBPasswdFile) +FN_GLOBAL_STRING(lp_serverstring,&Globals.szServerString) +FN_GLOBAL_STRING(lp_printcapname,&Globals.szPrintcapname) +FN_GLOBAL_STRING(lp_lockdir,&Globals.szLockDir) +FN_GLOBAL_STRING(lp_rootdir,&Globals.szRootdir) +FN_GLOBAL_STRING(lp_defaultservice,&Globals.szDefaultService) +FN_GLOBAL_STRING(lp_msg_command,&Globals.szMsgCommand) +FN_GLOBAL_STRING(lp_dfree_command,&Globals.szDfree) +FN_GLOBAL_STRING(lp_hosts_equiv,&Globals.szHostsEquiv) +FN_GLOBAL_STRING(lp_auto_services,&Globals.szAutoServices) +FN_GLOBAL_STRING(lp_passwd_program,&Globals.szPasswdProgram) +FN_GLOBAL_STRING(lp_passwd_chat,&Globals.szPasswdChat) +FN_GLOBAL_STRING(lp_passwordserver,&Globals.szPasswordServer) +FN_GLOBAL_STRING(lp_workgroup,&Globals.szWorkGroup) +FN_GLOBAL_STRING(lp_domain_controller,&Globals.szDomainController) +FN_GLOBAL_STRING(lp_username_map,&Globals.szUsernameMap) +FN_GLOBAL_STRING(lp_character_set,&Globals.szCharacterSet) +FN_GLOBAL_STRING(lp_logon_script,&Globals.szLogonScript) + +FN_GLOBAL_BOOL(lp_domain_master,&Globals.bDomainMaster) +FN_GLOBAL_BOOL(lp_domain_logons,&Globals.bDomainLogons) +FN_GLOBAL_BOOL(lp_preferred_master,&Globals.bPreferredMaster) +FN_GLOBAL_BOOL(lp_load_printers,&Globals.bLoadPrinters) +FN_GLOBAL_BOOL(lp_use_rhosts,&Globals.bUseRhosts) +FN_GLOBAL_BOOL(lp_getwdcache,&use_getwd_cache) +FN_GLOBAL_BOOL(lp_readprediction,&Globals.bReadPrediction) +FN_GLOBAL_BOOL(lp_readbmpx,&Globals.bReadbmpx) +FN_GLOBAL_BOOL(lp_readraw,&Globals.bReadRaw) +FN_GLOBAL_BOOL(lp_writeraw,&Globals.bWriteRaw) +FN_GLOBAL_BOOL(lp_null_passwords,&Globals.bNullPasswords) +FN_GLOBAL_BOOL(lp_strip_dot,&Globals.bStripDot) +FN_GLOBAL_BOOL(lp_encrypted_passwords,&Globals.bEncryptPasswords) +FN_GLOBAL_BOOL(lp_syslog_only,&Globals.bSyslogOnly) +FN_GLOBAL_BOOL(lp_browse_list,&Globals.bBrowseList) + +FN_GLOBAL_INTEGER(lp_os_level,&Globals.os_level) +FN_GLOBAL_INTEGER(lp_max_ttl,&Globals.max_ttl) +FN_GLOBAL_INTEGER(lp_max_log_size,&Globals.max_log_size) +FN_GLOBAL_INTEGER(lp_mangledstack,&Globals.mangled_stack) +FN_GLOBAL_INTEGER(lp_maxxmit,&Globals.max_xmit) +FN_GLOBAL_INTEGER(lp_maxmux,&Globals.max_mux) +FN_GLOBAL_INTEGER(lp_maxpacket,&Globals.max_packet) +FN_GLOBAL_INTEGER(lp_keepalive,&keepalive) +FN_GLOBAL_INTEGER(lp_passwordlevel,&Globals.pwordlevel) +FN_GLOBAL_INTEGER(lp_deadtime,&Globals.deadtime) +FN_GLOBAL_INTEGER(lp_maxprotocol,&Globals.maxprotocol) +FN_GLOBAL_INTEGER(lp_security,&Globals.security) +FN_GLOBAL_INTEGER(lp_printing,&Globals.printing) +FN_GLOBAL_INTEGER(lp_maxdisksize,&Globals.maxdisksize) +FN_GLOBAL_INTEGER(lp_lpqcachetime,&Globals.lpqcachetime) +FN_GLOBAL_INTEGER(lp_syslog,&Globals.syslog) + +FN_LOCAL_STRING(lp_preexec,szPreExec) +FN_LOCAL_STRING(lp_postexec,szPostExec) +FN_LOCAL_STRING(lp_rootpreexec,szRootPreExec) +FN_LOCAL_STRING(lp_rootpostexec,szRootPostExec) +FN_LOCAL_STRING(lp_servicename,szService) +FN_LOCAL_STRING(lp_pathname,szPath) +FN_LOCAL_STRING(lp_dontdescend,szDontdescend) +FN_LOCAL_STRING(lp_username,szUsername) +FN_LOCAL_STRING(lp_guestaccount,szGuestaccount) +FN_LOCAL_STRING(lp_invalid_users,szInvalidUsers) +FN_LOCAL_STRING(lp_valid_users,szValidUsers) +FN_LOCAL_STRING(lp_admin_users,szAdminUsers) +FN_LOCAL_STRING(lp_printcommand,szPrintcommand) +FN_LOCAL_STRING(lp_lpqcommand,szLpqcommand) +FN_LOCAL_STRING(lp_lprmcommand,szLprmcommand) +FN_LOCAL_STRING(lp_lppausecommand,szLppausecommand) +FN_LOCAL_STRING(lp_lpresumecommand,szLpresumecommand) +FN_LOCAL_STRING(lp_printername,szPrintername) +FN_LOCAL_STRING(lp_hostsallow,szHostsallow) +FN_LOCAL_STRING(lp_hostsdeny,szHostsdeny) +FN_LOCAL_STRING(lp_magicscript,szMagicScript) +FN_LOCAL_STRING(lp_magicoutput,szMagicOutput) +FN_LOCAL_STRING(lp_comment,comment) +FN_LOCAL_STRING(lp_force_user,force_user) +FN_LOCAL_STRING(lp_force_group,force_group) +FN_LOCAL_STRING(lp_readlist,readlist) +FN_LOCAL_STRING(lp_writelist,writelist) +FN_LOCAL_STRING(lp_volume,volume) +FN_LOCAL_STRING(lp_mangled_map,szMangledMap) + +FN_LOCAL_BOOL(lp_alternate_permissions,bAlternatePerm) +FN_LOCAL_BOOL(lp_revalidate,bRevalidate) +FN_LOCAL_BOOL(lp_casesensitive,bCaseSensitive) +FN_LOCAL_BOOL(lp_preservecase,bCasePreserve) +FN_LOCAL_BOOL(lp_shortpreservecase,bShortCasePreserve) +FN_LOCAL_BOOL(lp_casemangle,bCaseMangle) +FN_LOCAL_BOOL(lp_status,status) +FN_LOCAL_BOOL(lp_hide_dot_files,bHideDotFiles) +FN_LOCAL_BOOL(lp_browseable,bBrowseable) +FN_LOCAL_BOOL(lp_readonly,bRead_only) +FN_LOCAL_BOOL(lp_no_set_dir,bNo_set_dir) +FN_LOCAL_BOOL(lp_guest_ok,bGuest_ok) +FN_LOCAL_BOOL(lp_guest_only,bGuest_only) +FN_LOCAL_BOOL(lp_print_ok,bPrint_ok) +FN_LOCAL_BOOL(lp_postscript,bPostscript) +FN_LOCAL_BOOL(lp_map_hidden,bMap_hidden) +FN_LOCAL_BOOL(lp_map_archive,bMap_archive) +FN_LOCAL_BOOL(lp_locking,bLocking) +FN_LOCAL_BOOL(lp_strict_locking,bStrictLocking) +FN_LOCAL_BOOL(lp_share_modes,bShareModes) +FN_LOCAL_BOOL(lp_onlyuser,bOnlyUser) +FN_LOCAL_BOOL(lp_manglednames,bMangledNames) +FN_LOCAL_BOOL(lp_widelinks,bWidelinks) +FN_LOCAL_BOOL(lp_syncalways,bSyncAlways) +FN_LOCAL_BOOL(lp_map_system,bMap_system) + +FN_LOCAL_INTEGER(lp_create_mode,iCreate_mode) +FN_LOCAL_INTEGER(lp_max_connections,iMaxConnections) +FN_LOCAL_INTEGER(lp_defaultcase,iDefaultCase) +FN_LOCAL_INTEGER(lp_minprintspace,iMinPrintSpace) + +FN_LOCAL_CHAR(lp_magicchar,magic_char) + + + +/* local prototypes */ +static int strwicmp( char *psz1, char *psz2 ); +static int map_parameter( char *pszParmName); +static BOOL set_boolean( BOOL *pb, char *pszParmValue ); +static int getservicebyname(char *pszServiceName, service *pserviceDest); +static void copy_service( service *pserviceDest, + service *pserviceSource, + BOOL *pcopymapDest ); +static BOOL service_ok(int iService); +static BOOL do_parameter(char *pszParmName, char *pszParmValue); +static BOOL do_section(char *pszSectionName); +static void dump_globals(void); +static void dump_a_service(service *pService); +static void init_copymap(service *pservice); + + +/*************************************************************************** +initialise a service to the defaults +***************************************************************************/ +static void init_service(service *pservice) +{ + bzero((char *)pservice,sizeof(service)); + copy_service(pservice,&sDefault,NULL); +} + + +/*************************************************************************** +free the dynamically allocated parts of a service struct +***************************************************************************/ +static void free_service(service *pservice) +{ + int i; + if (!pservice) + return; + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].type == P_STRING && parm_table[i].class == P_LOCAL) + string_free((char **)(((char *)pservice) + PTR_DIFF(parm_table[i].ptr,&sDefault))); +} + +/*************************************************************************** +add a new service to the services array initialising it with the given +service +***************************************************************************/ +static int add_a_service(service *pservice, char *name) +{ + int i; + service tservice; + int num_to_alloc = iNumServices+1; + + tservice = *pservice; + + /* it might already exist */ + if (name) + { + i = getservicebyname(name,NULL); + if (i >= 0) + return(i); + } + + /* find an invalid one */ + for (i=0;i<iNumServices;i++) + if (!pSERVICE(i)->valid) + break; + + /* if not, then create one */ + if (i == iNumServices) + { + ServicePtrs = (service **)Realloc(ServicePtrs,sizeof(service *)*num_to_alloc); + if (ServicePtrs) + pSERVICE(iNumServices) = (service *)malloc(sizeof(service)); + + if (!ServicePtrs || !pSERVICE(iNumServices)) + return(-1); + + iNumServices++; + } + else + free_service(pSERVICE(i)); + + pSERVICE(i)->valid = True; + + init_service(pSERVICE(i)); + copy_service(pSERVICE(i),&tservice,NULL); + if (name) + string_set(&iSERVICE(i).szService,name); + + return(i); +} + +/*************************************************************************** +add a new home service, with the specified home directory, defaults coming +from service ifrom +***************************************************************************/ +BOOL lp_add_home(char *pszHomename, int iDefaultService, char *pszHomedir) +{ + int i = add_a_service(pSERVICE(iDefaultService),pszHomename); + + if (i < 0) + return(False); + + if (!(*(iSERVICE(i).szPath)) || strequal(iSERVICE(i).szPath,lp_pathname(-1))) + string_set(&iSERVICE(i).szPath,pszHomedir); + if (!(*(iSERVICE(i).comment))) + { + pstring comment; + sprintf(comment,"Home directory of %s",pszHomename); + string_set(&iSERVICE(i).comment,comment); + } + iSERVICE(i).bAvailable = sDefault.bAvailable; + iSERVICE(i).bBrowseable = sDefault.bBrowseable; + + DEBUG(3,("adding home directory %s at %s\n", pszHomename, pszHomedir)); + + return(True); +} + +/*************************************************************************** +add a new service, based on an old one +***************************************************************************/ +int lp_add_service(char *pszService, int iDefaultService) +{ + return(add_a_service(pSERVICE(iDefaultService),pszService)); +} + + +/*************************************************************************** +add the IPC service +***************************************************************************/ +static BOOL lp_add_ipc(void) +{ + pstring comment; + int i = add_a_service(&sDefault,"IPC$"); + + if (i < 0) + return(False); + + sprintf(comment,"IPC Service (%s)",lp_serverstring()); + + string_set(&iSERVICE(i).szPath,"/tmp"); + string_set(&iSERVICE(i).szUsername,""); + string_set(&iSERVICE(i).comment,comment); + iSERVICE(i).status = False; + iSERVICE(i).iMaxConnections = 0; + iSERVICE(i).bAvailable = True; + iSERVICE(i).bRead_only = True; + iSERVICE(i).bGuest_only = False; + iSERVICE(i).bGuest_ok = True; + iSERVICE(i).bPrint_ok = False; + iSERVICE(i).bBrowseable = sDefault.bBrowseable; + + DEBUG(3,("adding IPC service\n")); + + return(True); +} + + +/*************************************************************************** +add a new printer service, with defaults coming from service iFrom +***************************************************************************/ +BOOL lp_add_printer(char *pszPrintername, int iDefaultService) +{ + char *comment = "From Printcap"; + int i = add_a_service(pSERVICE(iDefaultService),pszPrintername); + + if (i < 0) + return(False); + + /* note that we do NOT default the availability flag to True - */ + /* we take it from the default service passed. This allows all */ + /* dynamic printers to be disabled by disabling the [printers] */ + /* entry (if/when the 'available' keyword is implemented!). */ + + /* the printer name is set to the service name. */ + string_set(&iSERVICE(i).szPrintername,pszPrintername); + string_set(&iSERVICE(i).comment,comment); + iSERVICE(i).bBrowseable = sDefault.bBrowseable; + + DEBUG(3,("adding printer service %s\n",pszPrintername)); + + return(True); +} + + +/*************************************************************************** +Do a case-insensitive, whitespace-ignoring string compare. +***************************************************************************/ +static int strwicmp(char *psz1, char *psz2) +{ + /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */ + /* appropriate value. */ + if (psz1 == psz2) + return (0); + else + if (psz1 == NULL) + return (-1); + else + if (psz2 == NULL) + return (1); + + /* sync the strings on first non-whitespace */ + while (1) + { + while (isspace(*psz1)) + psz1++; + while (isspace(*psz2)) + psz2++; + if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' || *psz2 == '\0') + break; + psz1++; + psz2++; + } + return (*psz1 - *psz2); +} + +/*************************************************************************** +Map a parameter's string representation to something we can use. +Returns False if the parameter string is not recognised, else TRUE. +***************************************************************************/ +static int map_parameter(char *pszParmName) +{ + int iIndex; + + if (*pszParmName == '-') + return(-1); + + for (iIndex = 0; parm_table[iIndex].label; iIndex++) + if (strwicmp(parm_table[iIndex].label, pszParmName) == 0) + return(iIndex); + + DEBUG(0,( "Unknown parameter encountered: \"%s\"\n", pszParmName)); + return(-1); +} + + +/*************************************************************************** +Set a boolean variable from the text value stored in the passed string. +Returns True in success, False if the passed string does not correctly +represent a boolean. +***************************************************************************/ +static BOOL set_boolean(BOOL *pb, char *pszParmValue) +{ + BOOL bRetval; + + bRetval = True; + if (strwicmp(pszParmValue, "yes") == 0 || + strwicmp(pszParmValue, "true") == 0 || + strwicmp(pszParmValue, "1") == 0) + *pb = True; + else + if (strwicmp(pszParmValue, "no") == 0 || + strwicmp(pszParmValue, "False") == 0 || + strwicmp(pszParmValue, "0") == 0) + *pb = False; + else + { + DEBUG(0,( "Badly formed boolean in configuration file: \"%s\".\n", + pszParmValue)); + bRetval = False; + } + return (bRetval); +} + +/*************************************************************************** +Find a service by name. Otherwise works like get_service. +***************************************************************************/ +static int getservicebyname(char *pszServiceName, service *pserviceDest) +{ + int iService; + + for (iService = iNumServices - 1; iService >= 0; iService--) + if (VALID(iService) && + strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) + { + if (pserviceDest != NULL) + copy_service(pserviceDest, pSERVICE(iService), NULL); + break; + } + + return (iService); +} + + + +/*************************************************************************** +Copy a service structure to another + +If pcopymapDest is NULL then copy all fields +***************************************************************************/ +static void copy_service(service *pserviceDest, + service *pserviceSource, + BOOL *pcopymapDest) +{ + int i; + BOOL bcopyall = (pcopymapDest == NULL); + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].ptr && parm_table[i].class == P_LOCAL && + (bcopyall || pcopymapDest[i])) + { + void *def_ptr = parm_table[i].ptr; + void *src_ptr = + ((char *)pserviceSource) + PTR_DIFF(def_ptr,&sDefault); + void *dest_ptr = + ((char *)pserviceDest) + PTR_DIFF(def_ptr,&sDefault); + + switch (parm_table[i].type) + { + case P_BOOL: + case P_BOOLREV: + *(BOOL *)dest_ptr = *(BOOL *)src_ptr; + break; + + case P_INTEGER: + case P_OCTAL: + *(int *)dest_ptr = *(int *)src_ptr; + break; + + case P_CHAR: + *(char *)dest_ptr = *(char *)src_ptr; + break; + + case P_STRING: + string_set(dest_ptr,*(char **)src_ptr); + break; + default: + break; + } + } + + if (bcopyall) + { + init_copymap(pserviceDest); + if (pserviceSource->copymap) + memcpy((void *)pserviceDest->copymap, + (void *)pserviceSource->copymap,sizeof(BOOL)*NUMPARAMETERS); + } +} + +/*************************************************************************** +Check a service for consistency. Return False if the service is in any way +incomplete or faulty, else True. +***************************************************************************/ +static BOOL service_ok(int iService) +{ + BOOL bRetval; + + bRetval = True; + if (iSERVICE(iService).szService[0] == '\0') + { + DEBUG(0,( "The following message indicates an internal error:\n")); + DEBUG(0,( "No service name in service entry.\n")); + bRetval = False; + } + + /* The [printers] entry MUST be printable. I'm all for flexibility, but */ + /* I can't see why you'd want a non-printable printer service... */ + if (strwicmp(iSERVICE(iService).szService,PRINTERS_NAME) == 0) + if (!iSERVICE(iService).bPrint_ok) + { + DEBUG(0,( "WARNING: [%s] service MUST be printable!\n", + iSERVICE(iService).szService)); + iSERVICE(iService).bPrint_ok = True; + } + + if (iSERVICE(iService).szPath[0] == '\0' && + strwicmp(iSERVICE(iService).szService,HOMES_NAME) != 0) + { + DEBUG(0,("No path in service %s - using /tmp\n",iSERVICE(iService).szService)); + string_set(&iSERVICE(iService).szPath,"/tmp"); + } + + /* If a service is flagged unavailable, log the fact at level 0. */ + if (!iSERVICE(iService).bAvailable) + DEBUG(1,( "NOTE: Service %s is flagged unavailable.\n", + iSERVICE(iService).szService)); + + return (bRetval); +} + +static struct file_lists { + struct file_lists *next; + char *name; + time_t modtime; +} *file_lists = NULL; + +/******************************************************************* +keep a linked list of all config files so we know when one has changed +it's date and needs to be reloaded +********************************************************************/ +static void add_to_file_list(char *fname) +{ + struct file_lists *f=file_lists; + + while (f) { + if (f->name && !strcmp(f->name,fname)) break; + f = f->next; + } + + if (!f) { + f = (struct file_lists *)malloc(sizeof(file_lists[0])); + if (!f) return; + f->next = file_lists; + f->name = strdup(fname); + if (!f->name) { + free(f); + return; + } + file_lists = f; + } + + { + pstring n2; + strcpy(n2,fname); + standard_sub_basic(n2); + f->modtime = file_modtime(n2); + } + +} + +/******************************************************************* +check if a config file has changed date +********************************************************************/ +BOOL lp_file_list_changed(void) +{ + struct file_lists *f = file_lists; + while (f) { + pstring n2; + strcpy(n2,f->name); + standard_sub_basic(n2); + if (f->modtime != file_modtime(n2)) return(True); + f = f->next; + } + return(False); +} + +#ifdef KANJI +/*************************************************************************** + handle the interpretation of the coding system parameter + *************************************************************************/ +static BOOL handle_coding_system(char *pszParmValue,int *val) +{ + *val = interpret_coding_system(pszParmValue,*val); + return(True); +} +#endif /* KANJI */ + +/*************************************************************************** +handle the interpretation of the character set system parameter +***************************************************************************/ +static BOOL handle_character_set(char *pszParmValue,int *val) +{ + string_set(&Globals.szCharacterSet,pszParmValue); + *val = interpret_character_set(pszParmValue,*val); + return(True); +} + + +/*************************************************************************** +handle the interpretation of the protocol parameter +***************************************************************************/ +static BOOL handle_protocol(char *pszParmValue,int *val) +{ + *val = interpret_protocol(pszParmValue,*val); + return(True); +} + +/*************************************************************************** +handle the interpretation of the security parameter +***************************************************************************/ +static BOOL handle_security(char *pszParmValue,int *val) +{ + *val = interpret_security(pszParmValue,*val); + return(True); +} + +/*************************************************************************** +handle the interpretation of the default case +***************************************************************************/ +static BOOL handle_case(char *pszParmValue,int *val) +{ + if (strequal(pszParmValue,"LOWER")) + *val = CASE_LOWER; + else if (strequal(pszParmValue,"UPPER")) + *val = CASE_UPPER; + return(True); +} + +/*************************************************************************** +handle the interpretation of the printing system +***************************************************************************/ +static BOOL handle_printing(char *pszParmValue,int *val) +{ + if (strequal(pszParmValue,"sysv")) + *val = PRINT_SYSV; + else if (strequal(pszParmValue,"aix")) + *val = PRINT_AIX; + else if (strequal(pszParmValue,"hpux")) + *val = PRINT_HPUX; + else if (strequal(pszParmValue,"bsd")) + *val = PRINT_BSD; + else if (strequal(pszParmValue,"qnx")) + *val = PRINT_QNX; + return(True); +} + +/*************************************************************************** +handle the valid chars lines +***************************************************************************/ +static BOOL handle_valid_chars(char *pszParmValue,char **ptr) +{ + string_set(ptr,pszParmValue); + + add_char_string(pszParmValue); + return(True); +} + + +/*************************************************************************** +handle the include operation +***************************************************************************/ +static BOOL handle_include(char *pszParmValue,char **ptr) +{ + pstring fname; + strcpy(fname,pszParmValue); + + add_to_file_list(fname); + + standard_sub_basic(fname); + + string_set(ptr,fname); + + if (file_exist(fname,NULL)) + return(pm_process(fname, do_section, do_parameter)); + + DEBUG(2,("Can't find include file %s\n",fname)); + + return(False); +} + + +/*************************************************************************** +handle the interpretation of the copy parameter +***************************************************************************/ +static BOOL handle_copy(char *pszParmValue,char **ptr) +{ + BOOL bRetval; + int iTemp; + service serviceTemp; + + string_set(ptr,pszParmValue); + + init_service(&serviceTemp); + + bRetval = False; + + DEBUG(3,("Copying service from service %s\n",pszParmValue)); + + if ((iTemp = getservicebyname(pszParmValue, &serviceTemp)) >= 0) + { + if (iTemp == iServiceIndex) + { + DEBUG(0,("Can't copy service %s - unable to copy self!\n", + pszParmValue)); + } + else + { + copy_service(pSERVICE(iServiceIndex), + &serviceTemp, + iSERVICE(iServiceIndex).copymap); + bRetval = True; + } + } + else + { + DEBUG(0,( "Unable to copy service - source not found: %s\n", + pszParmValue)); + bRetval = False; + } + + free_service(&serviceTemp); + return (bRetval); +} + + +/*************************************************************************** +initialise a copymap +***************************************************************************/ +static void init_copymap(service *pservice) +{ + int i; + if (pservice->copymap) free(pservice->copymap); + pservice->copymap = (BOOL *)malloc(sizeof(BOOL)*NUMPARAMETERS); + if (!pservice->copymap) + DEBUG(0,("Couldn't allocate copymap!! (size %d)\n",NUMPARAMETERS)); + + for (i=0;i<NUMPARAMETERS;i++) + pservice->copymap[i] = True; +} + + +/*************************************************************************** +Process a parameter. +***************************************************************************/ +static BOOL do_parameter(char *pszParmName, char *pszParmValue) +{ + int parmnum; + void *parm_ptr=NULL; /* where we are going to store the result */ + void *def_ptr=NULL; + + if (!bInGlobalSection && bGlobalOnly) return(True); + + DEBUG(3,("doing parameter %s = %s\n",pszParmName,pszParmValue)); + + parmnum = map_parameter(pszParmName); + + if (parmnum < 0) + { + DEBUG(0,( "Ignoring unknown parameter \"%s\"\n", pszParmName)); + return(True); + } + + def_ptr = parm_table[parmnum].ptr; + + /* we might point at a service, the default service or a global */ + if (bInGlobalSection) + parm_ptr = def_ptr; + else + { + if (parm_table[parmnum].class == P_GLOBAL) + { + DEBUG(0,( "Global parameter %s found in service section!\n",pszParmName)); + return(True); + } + parm_ptr = ((char *)pSERVICE(iServiceIndex)) + PTR_DIFF(def_ptr,&sDefault); + } + + if (!bInGlobalSection) + { + int i; + if (!iSERVICE(iServiceIndex).copymap) + init_copymap(pSERVICE(iServiceIndex)); + + /* this handles the aliases - set the copymap for other entries with + the same data pointer */ + for (i=0;parm_table[i].label;i++) + if (parm_table[i].ptr == parm_table[parmnum].ptr) + iSERVICE(iServiceIndex).copymap[i] = False; + } + + /* if it is a special case then go ahead */ + if (parm_table[parmnum].special) + { + parm_table[parmnum].special(pszParmValue,parm_ptr); + return(True); + } + + /* now switch on the type of variable it is */ + switch (parm_table[parmnum].type) + { + case P_BOOL: + set_boolean(parm_ptr,pszParmValue); + break; + + case P_BOOLREV: + set_boolean(parm_ptr,pszParmValue); + *(BOOL *)parm_ptr = ! *(BOOL *)parm_ptr; + break; + + case P_INTEGER: + *(int *)parm_ptr = atoi(pszParmValue); + break; + + case P_CHAR: + *(char *)parm_ptr = *pszParmValue; + break; + + case P_OCTAL: + sscanf(pszParmValue,"%o",(int *)parm_ptr); + break; + + case P_STRING: + string_set(parm_ptr,pszParmValue); + break; + + case P_GSTRING: + strcpy((char *)parm_ptr,pszParmValue); + break; + } + + return(True); +} + +/*************************************************************************** +print a parameter of the specified type +***************************************************************************/ +static void print_parameter(parm_type type,void *ptr) +{ + switch (type) + { + case P_BOOL: + printf("%s",BOOLSTR(*(BOOL *)ptr)); + break; + + case P_BOOLREV: + printf("%s",BOOLSTR(! *(BOOL *)ptr)); + break; + + case P_INTEGER: + printf("%d",*(int *)ptr); + break; + + case P_CHAR: + printf("%c",*(char *)ptr); + break; + + case P_OCTAL: + printf("0%o",*(int *)ptr); + break; + + case P_GSTRING: + if ((char *)ptr) + printf("%s",(char *)ptr); + break; + + case P_STRING: + if (*(char **)ptr) + printf("%s",*(char **)ptr); + break; + } +} + + +/*************************************************************************** +check if two parameters are equal +***************************************************************************/ +static BOOL equal_parameter(parm_type type,void *ptr1,void *ptr2) +{ + switch (type) + { + case P_BOOL: + case P_BOOLREV: + return(*((BOOL *)ptr1) == *((BOOL *)ptr2)); + + case P_INTEGER: + case P_OCTAL: + return(*((int *)ptr1) == *((int *)ptr2)); + + case P_CHAR: + return(*((char *)ptr1) == *((char *)ptr2)); + + case P_GSTRING: + { + char *p1 = (char *)ptr1, *p2 = (char *)ptr2; + if (p1 && !*p1) p1 = NULL; + if (p2 && !*p2) p2 = NULL; + return(p1==p2 || strequal(p1,p2)); + } + case P_STRING: + { + char *p1 = *(char **)ptr1, *p2 = *(char **)ptr2; + if (p1 && !*p1) p1 = NULL; + if (p2 && !*p2) p2 = NULL; + return(p1==p2 || strequal(p1,p2)); + } + } + return(False); +} + +/*************************************************************************** +Process a new section (service). At this stage all sections are services. +Later we'll have special sections that permit server parameters to be set. +Returns True on success, False on failure. +***************************************************************************/ +static BOOL do_section(char *pszSectionName) +{ + BOOL bRetval; + BOOL isglobal = ((strwicmp(pszSectionName, GLOBAL_NAME) == 0) || + (strwicmp(pszSectionName, GLOBAL_NAME2) == 0)); + bRetval = False; + + /* if we were in a global section then do the local inits */ + if (bInGlobalSection && !isglobal) + init_locals(); + + /* if we've just struck a global section, note the fact. */ + bInGlobalSection = isglobal; + + /* check for multiple global sections */ + if (bInGlobalSection) + { + DEBUG(3,( "Processing section \"[%s]\"\n", pszSectionName)); + return(True); + } + + if (!bInGlobalSection && bGlobalOnly) return(True); + + /* if we have a current service, tidy it up before moving on */ + bRetval = True; + + if (iServiceIndex >= 0) + bRetval = service_ok(iServiceIndex); + + /* if all is still well, move to the next record in the services array */ + if (bRetval) + { + /* We put this here to avoid an odd message order if messages are */ + /* issued by the post-processing of a previous section. */ + DEBUG(2,( "Processing section \"[%s]\"\n", pszSectionName)); + + if ((iServiceIndex=add_a_service(&sDefault,pszSectionName)) < 0) + { + DEBUG(0,("Failed to add a new service\n")); + return(False); + } + } + + return (bRetval); +} + +/*************************************************************************** +Display the contents of the global structure. +***************************************************************************/ +static void dump_globals(void) +{ + int i; + printf("Global parameters:\n"); + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].class == P_GLOBAL && + parm_table[i].ptr && + (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr))) + { + printf("\t%s: ",parm_table[i].label); + print_parameter(parm_table[i].type,parm_table[i].ptr); + printf("\n"); + } +} + +/*************************************************************************** +Display the contents of a single services record. +***************************************************************************/ +static void dump_a_service(service *pService) +{ + int i; + if (pService == &sDefault) + printf("\nDefault service parameters:\n"); + else + printf("\nService parameters [%s]:\n",pService->szService); + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].class == P_LOCAL && + parm_table[i].ptr && + (*parm_table[i].label != '-') && + (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr))) + { + int pdiff = PTR_DIFF(parm_table[i].ptr,&sDefault); + + if (pService == &sDefault || !equal_parameter(parm_table[i].type, + ((char *)pService) + pdiff, + ((char *)&sDefault) + pdiff)) + { + printf("\t%s: ",parm_table[i].label); + print_parameter(parm_table[i].type, + ((char *)pService) + pdiff); + printf("\n"); + } + } +} + +#if 0 +/*************************************************************************** +Display the contents of a single copy structure. +***************************************************************************/ +static void dump_copy_map(BOOL *pcopymap) +{ + int i; + if (!pcopymap) return; + + printf("\n\tNon-Copied parameters:\n"); + + for (i=0;parm_table[i].label;i++) + if (parm_table[i].class == P_LOCAL && + parm_table[i].ptr && !pcopymap[i] && + (i == 0 || (parm_table[i].ptr != parm_table[i-1].ptr))) + { + printf("\t\t%s\n",parm_table[i].label); + } +} +#endif + +/*************************************************************************** +Return TRUE if the passed service number is within range. +***************************************************************************/ +BOOL lp_snum_ok(int iService) +{ + return (LP_SNUM_OK(iService) && iSERVICE(iService).bAvailable); +} + + +/*************************************************************************** +auto-load some homes and printer services +***************************************************************************/ +static void lp_add_auto_services(char *str) +{ + char *s; + char *p; + int homes = lp_servicenumber(HOMES_NAME); + int printers = lp_servicenumber(PRINTERS_NAME); + + if (!str) + return; + + s = strdup(str); + if (!s) return; + + for (p=strtok(s,LIST_SEP);p;p=strtok(NULL,LIST_SEP)) + { + char *home = get_home_dir(p); + + if (lp_servicenumber(p) >= 0) continue; + + if (home && homes >= 0) + { + lp_add_home(p,homes,home); + continue; + } + + if (printers >= 0 && pcap_printername_ok(p,NULL)) + lp_add_printer(p,printers); + } + free(s); +} + +/*************************************************************************** +auto-load one printer +***************************************************************************/ +static void lp_add_one_printer(char *name,char *comment) +{ + int printers = lp_servicenumber(PRINTERS_NAME); + int i; + + if (lp_servicenumber(name) < 0) + { + lp_add_printer(name,printers); + if ((i=lp_servicenumber(name)) >= 0) + string_set(&iSERVICE(i).comment,comment); + } +} + + +/*************************************************************************** +auto-load printer services +***************************************************************************/ +static void lp_add_all_printers(void) +{ + int printers = lp_servicenumber(PRINTERS_NAME); + + if (printers < 0) return; + + pcap_printer_fn(lp_add_one_printer); +} + +/*************************************************************************** +have we loaded a services file yet? +***************************************************************************/ +BOOL lp_loaded(void) +{ + return(bLoaded); +} + +/*************************************************************************** +unload unused services +***************************************************************************/ +void lp_killunused(BOOL (*snumused)(int )) +{ + int i; + for (i=0;i<iNumServices;i++) + if (VALID(i) && !snumused(i)) + { + iSERVICE(i).valid = False; + free_service(pSERVICE(i)); + } +} + +/*************************************************************************** +Load the services array from the services file. Return True on success, +False on failure. +***************************************************************************/ +BOOL lp_load(char *pszFname,BOOL global_only) +{ + pstring n2; + BOOL bRetval; + + add_to_file_list(pszFname); + + bRetval = False; + + bInGlobalSection = True; + bGlobalOnly = global_only; + + init_globals(); + + strcpy(n2,pszFname); + standard_sub_basic(n2); + + /* We get sections first, so have to start 'behind' to make up */ + iServiceIndex = -1; + bRetval = pm_process(n2, do_section, do_parameter); + + /* finish up the last section */ + DEBUG(3,("pm_process() returned %s\n", BOOLSTR(bRetval))); + if (bRetval) + if (iServiceIndex >= 0) + bRetval = service_ok(iServiceIndex); + + lp_add_auto_services(lp_auto_services()); + if (lp_load_printers()) + lp_add_all_printers(); + + lp_add_ipc(); + + bLoaded = True; + + return (bRetval); +} + + +/*************************************************************************** +return the max number of services +***************************************************************************/ +int lp_numservices(void) +{ + return(iNumServices); +} + +/*************************************************************************** +Display the contents of the services array in human-readable form. +***************************************************************************/ +void lp_dump(void) +{ + int iService; + + dump_globals(); + + dump_a_service(&sDefault); + + for (iService = 0; iService < iNumServices; iService++) + { + if (VALID(iService)) + { + if (iSERVICE(iService).szService[0] == '\0') + break; + dump_a_service(pSERVICE(iService)); + } + } +} + +/*************************************************************************** +Return the number of the service with the given name, or -1 if it doesn't +exist. Note that this is a DIFFERENT ANIMAL from the internal function +getservicebyname()! This works ONLY if all services have been loaded, and +does not copy the found service. +***************************************************************************/ +int lp_servicenumber(char *pszServiceName) +{ + int iService; + + for (iService = iNumServices - 1; iService >= 0; iService--) + if (VALID(iService) && + strwicmp(iSERVICE(iService).szService, pszServiceName) == 0) + break; + + if (iService < 0) + DEBUG(7,("lp_servicenumber: couldn't find %s\n",pszServiceName)); + + return (iService); +} + + + + +/******************************************************************* + get a workgroup - but map to standalone if '*' + ******************************************************************/ +char *my_workgroup(void) +{ + char *res = lp_workgroup(); + if (*res == '*') return("STANDALONE"); + return(res); +} + +/******************************************************************* + a useful volume label function + ******************************************************************/ +char *volume_label(int snum) +{ + char *ret = lp_volume(snum); + if (!*ret) return(lp_servicename(snum)); + return(ret); +} diff --git a/source3/param/params.c b/source3/param/params.c new file mode 100644 index 0000000000..b9d61382a1 --- /dev/null +++ b/source3/param/params.c @@ -0,0 +1,335 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Parameter loading utlities + Copyright (C) Karl Auer 1993,1994 + + 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. +*/ + +/************************************************************************** +PARAMS.C + +Copyright (C) 1990, 1991, 1992, 1993, 1994 Karl Auer + +This module provides for streamlines retrieval of information from a +Windows-like parameter files. There is a function which will search for +all sections in the file and call a specified function with each. There is +a similar function which will call a specified function for all parameters +in a section. The idea is that you pass the addresses of suitable functions +to a single function in this module which will then enumerate all sections, +and within each section all parameters, to your program. + +Parameter files contain text lines (newline delimited) which consist of +either a section name in square brackets or a parameter name, delimited +from the parameter value by an equals sign. Blank lines or lines where the +first non-whitespace character is a colon are ignored. All whitespace in +section names and parameter names is compressed to single spaces. Leading +and trailing whitespace on parameter names and parameter values is stripped. + +Only the first equals sign in a parameter line is significant - parameter +values may contain equals signs, square brackets and semicolons. Internal +whitespace is retained in parameter values. Parameter names may not start +with a square bracket, an equals sign or a semicolon, for obvious reasons. + +A sample parameter file might look like this: + +[things] +this=1 +that=2 +[other things] +the other = 3 + +**************************************************************************/ + +#include "includes.h" + +#include "smb.h" +#include "params.h" + +/* local variable pointing to passed filename */ +static char *pszParmFile = NULL; +extern int DEBUGLEVEL; + +/* local prototypes */ +static BOOL enumerate_parameters(FILE *infile, PM_PARMFUNC pfunc); +static BOOL enumerate_sections(FILE *infile, + PM_SECFUNC sfunc, PM_PARMFUNC pfunc); + +/* prototypes for local toolbox functions */ +static void trimleft(char *psz); +static void trimright(char *psz); +static void collapse_spaces(char *psz); +static int firstnonwhite(char *psz); + +/************************************************************************** +Identifies all parameters in the current section, calls the parameter +function for each. Ignores comment lines, stops and backs up in file when +a section is encountered. Returns True on success, False on error. +**************************************************************************/ +static BOOL enumerate_parameters(FILE *fileIn, PM_PARMFUNC pfunc) +{ + pstring szBuf; + char *pszTemp; + BOOL bRetval; + long lFileOffset; + int cTemp; + BOOL bParmFound; + + bRetval = False; + bParmFound = False; + while (True) + { + /* first remember where we are */ + if ((lFileOffset = ftell(fileIn)) >= 0L) + { + /* then get and check a line */ + if (fgets_slash(szBuf, sizeof(szBuf)-1, fileIn) == NULL) + { + /* stop - return OK unless file error */ + bRetval = !ferror(fileIn); + if (!bRetval) + DEBUG(0,( "Read error on configuration file (enumerating parameters)!\n")); + break; + } + else + /* if first non-white is a '[', stop (new section) */ + if ((cTemp = firstnonwhite(szBuf)) == '[') + { + /* restore position to start of new section */ + if (fseek(fileIn, lFileOffset, SEEK_SET) < 0L) + { + DEBUG(0,( "Seek error on configuration file!\n")); + break; + } + + /* return success */ + bRetval = True; + break; + } + else + /* if it's a semicolon or line is blank, ignore the line */ + if (!cTemp || strchr(";#",cTemp)) + { + continue; + } + else + /* if no equals sign and line contains non-whitespace */ + /* then line is badly formed */ + if ((pszTemp = strchr(szBuf, '=')) == NULL) + { + DEBUG(0,( "Ignoring badly formed line: %s", szBuf)); + } + else + { + /* Note that we have found a parameter */ + bParmFound = True; + /* cut line at the equals sign */ + *pszTemp++ = '\0'; + /* trim leading and trailing space from both halves */ + trimright(szBuf); + trimleft(szBuf); + trimright(pszTemp); + trimleft(pszTemp); + /* process the parameter iff passed pointer not NULL */ + if (pfunc != NULL) + if (!pfunc(szBuf, pszTemp)) + break; + } + } + } + return (bRetval); +} + + +/*********************************************************************** +Close up s by n chars, at offset start. +***********************************************************************/ +static void closestr(char *s, int start, int n) +{ + char *src; + char *dest; + int len; + + if (n > 0) + if ((src = dest = s) != NULL) + { + len = strlen(s); + if (start >= 0 && start < len - n) + { + src += start + n; + dest += start; + + while (*src) + *dest++ = *src++; + *dest = '\0'; + } + } +} + +/************************************************************************** +Identifies all sections in the parameter file, calls passed section_func() +for each, passing the section name, then calls enumerate_parameters(). +Returns True on success, False on failure. Note that the section and +parameter names will have all internal whitespace areas collapsed to a +single space for processing. +**************************************************************************/ +static BOOL enumerate_sections(FILE *fileIn, + PM_SECFUNC sfunc, PM_PARMFUNC pfunc) +{ + pstring szBuf; + BOOL bRetval; + BOOL bSectionFound; + + /* this makes sure we get include lines right */ + enumerate_parameters(fileIn, pfunc); + + bRetval = False; + bSectionFound = False; + while (True) + { + if (fgets_slash(szBuf, sizeof(szBuf)-1, fileIn) == NULL) + { + /* stop - return OK unless file error */ + bRetval = !ferror(fileIn); + if (!bRetval) + DEBUG(0,( "Read error on configuration file (enumerating sections)!\n")); + break; + } + else + { + trimleft(szBuf); + trimright(szBuf); + if (szBuf[0] == '[') + { + closestr(szBuf, 0, 1); + if (strlen(szBuf) > 1) + if (szBuf[strlen(szBuf) - 1] == ']') + { + /* found a section - note the fact */ + bSectionFound = True; + /* remove trailing metabracket */ + szBuf[strlen(szBuf) - 1] = '\0'; + /* remove leading and trailing whitespace from name */ + trimleft(szBuf); + trimright(szBuf); + /* reduce all internal whitespace to one space */ + collapse_spaces(szBuf); + /* process it - stop if the processing fails */ + if (sfunc != NULL) + if (!sfunc(szBuf)) + break; + if (!enumerate_parameters(fileIn, pfunc)) + break; + } + } + } + } + + return (bRetval); +} + +/************************************************************************** +Process the passed parameter file. + +Returns True if successful, else False. +**************************************************************************/ +BOOL pm_process(char *pszFileName, PM_SECFUNC sfunc, PM_PARMFUNC pfunc) +{ + FILE *fileIn; + BOOL bRetval; + + bRetval = False; + + /* record the filename for use in error messages one day... */ + pszParmFile = pszFileName; + + if (pszParmFile == NULL || strlen(pszParmFile) < 1) + DEBUG(0,( "No configuration filename specified!\n")); + else + if ((fileIn = fopen(pszParmFile, "r")) == NULL) + DEBUG(0,( "Unable to open configuration file \"%s\"!\n", pszParmFile)); + else + { + DEBUG(2,( "Processing configuration file \"%s\"\n", pszParmFile)); + bRetval = enumerate_sections(fileIn, sfunc, pfunc); + fclose(fileIn); + } + + if (!bRetval) + DEBUG(0,("pm_process retuned false\n")); + return (bRetval); +} + + +/************************************************************************** +Strip all leading whitespace from a string. +**************************************************************************/ +static void trimleft(char *psz) +{ + char *pszDest; + + pszDest = psz; + if (psz != NULL) + { + while (*psz != '\0' && isspace(*psz)) + psz++; + while (*psz != '\0') + *pszDest++ = *psz++; + *pszDest = '\0'; + } +} + +/************************************************************************** +Strip all trailing whitespace from a string. +**************************************************************************/ +static void trimright(char *psz) +{ + char *pszTemp; + + if (psz != NULL && psz[0] != '\0') + { + pszTemp = psz + strlen(psz) - 1; + while (isspace(*pszTemp)) + *pszTemp-- = '\0'; + } +} + +/*********************************************************************** +Collapse each whitespace area in a string to a single space. +***********************************************************************/ +static void collapse_spaces(char *psz) +{ + while (*psz) + if (isspace(*psz)) + { + *psz++ = ' '; + trimleft(psz); + } + else + psz++; +} + +/************************************************************************** +Return the value of the first non-white character in the specified string. +The terminating NUL counts as non-white for the purposes of this function. +Note - no check for a NULL string! What would we return? +**************************************************************************/ +static int firstnonwhite(char *psz) +{ + while (isspace(*psz) && (*psz != '\0')) + psz++; + return (*psz); +} diff --git a/source3/passdb/smbpass.c b/source3/passdb/smbpass.c new file mode 100644 index 0000000000..2dec15ffb4 --- /dev/null +++ b/source3/passdb/smbpass.c @@ -0,0 +1,304 @@ +#ifdef SMB_PASSWD +/* + * Unix SMB/Netbios implementation. Version 1.9. SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1995 Modified by Jeremy Allison 1995. + * + * 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. + */ + +#include "includes.h" +#include "loadparm.h" + +extern int DEBUGLEVEL; + +int gotalarm; + +void +gotalarm_sig() +{ + gotalarm = 1; +} + +int +do_pw_lock(int fd, int waitsecs, int type) +{ + struct flock lock; + int ret; + + gotalarm = 0; + signal(SIGALRM, SIGNAL_CAST gotalarm_sig); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 1; + lock.l_pid = 0; + + alarm(5); + ret = fcntl(fd, F_SETLKW, &lock); + alarm(0); + signal(SIGALRM, SIGNAL_CAST SIG_DFL); + + if (gotalarm) { + DEBUG(0, ("do_pw_lock: failed to %s SMB passwd file.\n", + type == F_UNLCK ? "unlock" : "lock")); + return -1; + } + return ret; +} + +int +pw_file_lock(char *name, int type, int secs) +{ + int fd = open(name, O_RDWR | O_CREAT, 0666); + if (fd < 0) + return (-1); + if (do_pw_lock(fd, secs, type)) { + close(fd); + return -1; + } + return fd; +} + +int +pw_file_unlock(int fd) +{ + do_pw_lock(fd, 5, F_UNLCK); + return close(fd); +} + +/* + * Routine to get the next 32 hex characters and turn them + * into a 16 byte array. + */ + +static int gethexpwd(char *p, char *pwd) +{ + int i; + unsigned char lonybble, hinybble; + char *hexchars = "0123456789ABCDEF"; + char *p1, *p2; + + for (i = 0; i < 32; i += 2) { + hinybble = toupper(p[i]); + lonybble = toupper(p[i + 1]); + + p1 = strchr(hexchars, hinybble); + p2 = strchr(hexchars, lonybble); + if (!p1 || !p2) + return (False); + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + pwd[i / 2] = (hinybble << 4) | lonybble; + } + return (True); +} + +/* + * Routine to search the smbpasswd file for an entry matching the username. + */ +struct smb_passwd * +get_smbpwnam(char *name) +{ + /* Static buffers we will return. */ + static struct smb_passwd pw_buf; + static pstring user_name; + static unsigned char smbpwd[16]; + static unsigned char smbntpwd[16]; + char linebuf[256]; + char readbuf[16 * 1024]; + unsigned char c; + unsigned char *p; + long uidval; + long linebuf_len; + FILE *fp; + int lockfd; + char *pfile = lp_smb_passwd_file(); + + if (!*pfile) { + DEBUG(0, ("No SMB password file set\n")); + return (NULL); + } + DEBUG(10, ("get_smbpwnam: opening file %s\n", pfile)); + + fp = fopen(pfile, "r"); + + if (fp == NULL) { + DEBUG(0, ("get_smbpwnam: unable to open file %s\n", pfile)); + return NULL; + } + /* Set a 16k buffer to do more efficient reads */ + setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); + + if ((lockfd = pw_file_lock(pfile, F_RDLCK, 5)) < 0) { + DEBUG(0, ("get_smbpwnam: unable to lock file %s\n", pfile)); + fclose(fp); + return NULL; + } + /* make sure it is only rw by the owner */ + chmod(pfile, 0600); + + /* We have a read lock on the file. */ + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + linebuf[0] = '\0'; + + fgets(linebuf, 256, fp); + if (ferror(fp)) { + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + linebuf_len = strlen(linebuf); + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') + break; + } + } else + linebuf[linebuf_len - 1] = '\0'; + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("get_smbpwnam: got line |%s|\n", linebuf)); +#endif + if ((linebuf[0] == 0) && feof(fp)) { + DEBUG(4, ("get_smbpwnam: end of file reached\n")); + break; + } + /* + * The line we have should be of the form :- + * + * username:uid:[32hex bytes]:....other flags presently + * ignored.... + * + * or, + * + * username:uid:[32hex bytes]:[32hex bytes]:....ignored.... + * + * if Windows NT compatible passwords are also present. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') { + DEBUG(6, ("get_smbpwnam: skipping comment or blank line\n")); + continue; + } + p = (unsigned char *) strchr(linebuf, ':'); + if (p == NULL) { + DEBUG(0, ("get_smbpwnam: malformed password entry (no :)\n")); + continue; + } + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + if (!strequal(user_name, name)) + continue; + + /* User name matches - get uid and password */ + p++; /* Go past ':' */ + if (!isdigit(*p)) { + DEBUG(0, ("get_smbpwnam: malformed password entry (uid not number)\n")); + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + uidval = atoi((char *) p); + while (*p && isdigit(*p)) + p++; + if (*p != ':') { + DEBUG(0, ("get_smbpwnam: malformed password entry (no : after uid)\n")); + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + p++; + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid - end here. */ + DEBUG(10, ("get_smbpwnam: entry invalidated for user %s\n", user_name)); + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) { + DEBUG(0, ("get_smbpwnam: malformed password entry (passwd too short)\n")); + fclose(fp); + pw_file_unlock(lockfd); + return (False); + } + if (p[32] != ':') { + DEBUG(0, ("get_smbpwnam: malformed password entry (no terminating :)\n")); + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + if (!strncasecmp((char *) p, "NO PASSWORD", 11)) { + pw_buf.smb_passwd = NULL; + } else { + if(!gethexpwd(p,smbpwd)) { + DEBUG(0, ("Malformed Lanman password entry (non hex chars)\n")); + fclose(fp); + pw_file_unlock(lockfd); + return NULL; + } + pw_buf.smb_passwd = smbpwd; + } + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + pw_buf.smb_nt_passwd = NULL; + + /* Now check if the NT compatible password is + available. */ + p += 33; /* Move to the first character of the line after + the lanman password. */ + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + if (*p != '*' && *p != 'X') { + if(gethexpwd(p,smbntpwd)) + pw_buf.smb_nt_passwd = smbntpwd; + } + } + + fclose(fp); + pw_file_unlock(lockfd); + DEBUG(5, ("get_smbpwname: returning passwd entry for user %s, uid %d\n", + user_name, uidval)); + return &pw_buf; + } + + fclose(fp); + pw_file_unlock(lockfd); + return NULL; +} +#else +void +smbpass_dummy(void) +{ +} /* To avoid compiler complaints */ +#endif diff --git a/source3/printing/pcap.c b/source3/printing/pcap.c new file mode 100644 index 0000000000..8973b1627f --- /dev/null +++ b/source3/printing/pcap.c @@ -0,0 +1,383 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + printcap parsing + Copyright (C) Karl Auer 1993,1994 + + Re-working by Martin Kiff, 1994 + + Re-written again by Andrew Tridgell + + 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. +*/ + +/* + * Parse printcap file. + * + * This module does exactly one thing - it looks into the printcap file + * and tells callers if a specified string appears as a printer name. + * + * The way this module looks at the printcap file is very simplistic. + * Only the local printcap file is inspected (no searching of NIS + * databases etc). + * + * There are assumed to be one or more printer names per record, held + * as a set of sub-fields separated by vertical bar symbols ('|') in the + * first field of the record. The field separator is assumed to be a colon + * ':' and the record separator a newline. + * + * Lines ending with a backspace '\' are assumed to flag that the following + * line is a continuation line so that a set of lines can be read as one + * printcap entry. + * + * A line stating with a hash '#' is assumed to be a comment and is ignored + * Comments are discarded before the record is strung together from the + * set of continuation lines. + * + * Opening a pipe for "lpc status" and reading that would probably + * be pretty effective. Code to do this already exists in the freely + * distributable PCNFS server code. + */ + +#include "includes.h" + +#include "smb.h" +#include "loadparm.h" +#include "pcap.h" + +extern int DEBUGLEVEL; + +#ifdef AIX +/* ****************************************** + Extend for AIX system and qconfig file + from 'boulard@univ-rennes1.fr + ****************************************** */ +static int strlocate(char *xpLine,char *xpS) +{ + int iS,iL,i,iRet; + char *p; + iS = strlen(xpS); + iL = strlen(xpLine); + + iRet = 0; + p = xpLine; + while (iL >= iS) + { + if (strncmp(p,xpS,iS) == 0) {iRet =1;break;}; + p++; + iL--; + } + /*DEBUG(3,(" strlocate %s in line '%s',ret=%d\n",xpS,xpLine,iRet));*/ + + return(iRet); +} + + +/* ******************************************************************* */ +/* * Scan qconfig and search all virtual printer (device printer) * */ +/* ******************************************************************* */ +static void ScanQconfig_fn(char *psz,void (*fn)()) +{ + int iLg,iEtat; + FILE *pfile; + char *line,*p; + pstring name,comment; + line = NULL; + *name = 0; + *comment = 0; + + if ((pfile = fopen(psz, "r")) == NULL) + { + DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz)); + return; + } + + iEtat = 0; + /* scan qconfig file for searching <printername>: */ + for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line)) + { + if (*line == '*' || *line == 0) + continue; + switch (iEtat) + { + case 0: /* locate an entry */ + if (*line == '\t' || *line == ' ') continue; + if ((p=strchr(line,':'))) + { + *p = '\0'; + p = strtok(line,":"); + if (strcmp(p,"bsh")!=0) + { + strcpy(name,p); + iEtat = 1; + continue; + } + } + break; + case 1: /* scanning device stanza */ + if (*line == '*' || *line == 0) continue; + if (*line != '\t' && *line != ' ') + { + /* name is found without stanza device */ + /* probably a good printer ??? */ + fn(name,comment); + iEtat = 0; + continue; + } + + if (strlocate(line,"backend")) + { + /* it's a device, not a virtual printer*/ + iEtat = 0; + } + else if (strlocate(line,"device")) + { + /* it's a good virtual printer */ + fn(name,comment); + iEtat = 0; + continue; + } + break; + } + } + fclose(pfile); +} + +/* Scan qconfig file and locate de printername */ + +static BOOL ScanQconfig(char *psz,char *pszPrintername) +{ + int iLg,iEtat; + FILE *pfile; + char *pName; + char *line; + + pName = NULL; + line = NULL; + if ((pszPrintername!= NULL) && ((iLg = strlen(pszPrintername)) > 0)) + pName = malloc(iLg+10); + if (pName == NULL) + { + DEBUG(0,(" Unable to allocate memory for printer %s\n",pszPrintername)); + return(False); + } + if ((pfile = fopen(psz, "r")) == NULL) + { + DEBUG(0,( "Unable to open qconfig file %s for read!\n", psz)); + free(pName); + return(False); + } + sprintf(pName,"%s:",pszPrintername); + iLg = strlen(pName); + /*DEBUG(3,( " Looking for entry %s\n",pName));*/ + iEtat = 0; + /* scan qconfig file for searching <printername>: */ + for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line)) + { + if (*line == '*' || *line == 0) + continue; + switch (iEtat) + { + case 0: /* scanning entry */ + if (strncmp(line,pName,iLg) == 0) + { + iEtat = 1; + continue; + } + break; + case 1: /* scanning device stanza */ + if (*line == '*' || *line == 0) continue; + if (*line != '\t' && *line != ' ') + { + /* name is found without stanza device */ + /* probably a good printer ??? */ + free (line); + free(pName); + fclose(pfile); + return(True); + } + + if (strlocate(line,"backend")) + { + /* it's a device, not a virtual printer*/ + iEtat = 0; + } + else if (strlocate(line,"device")) + { + /* it's a good virtual printer */ + free (line); + free(pName); + fclose(pfile); + return(True); + } + break; + } + } + free (pName); + fclose(pfile); + return(False); +} + +#endif +/*************************************************************************** +Scan printcap file pszPrintcapname for a printer called pszPrintername. +Return True if found, else False. Returns False on error, too, after logging +the error at level 0. For generality, the printcap name may be passed - if +passed as NULL, the configuration will be queried for the name. +***************************************************************************/ +BOOL pcap_printername_ok(char *pszPrintername, char *pszPrintcapname) +{ + char *line=NULL; + char *psz; + char *p,*q; + FILE *pfile; + + if (pszPrintername == NULL || pszPrintername[0] == '\0') + { + DEBUG(0,( "Attempt to locate null printername! Internal error?\n")); + return(False); + } + + /* only go looking if no printcap name supplied */ + if ((psz = pszPrintcapname) == NULL || psz[0] == '\0') + if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0')) + { + DEBUG(0,( "No printcap file name configured!\n")); + return(False); + } +#ifdef AIX + if (strlocate(psz,"/qconfig") != NULL) + return(ScanQconfig(psz,pszPrintername)); +#endif + if ((pfile = fopen(psz, "r")) == NULL) + { + DEBUG(0,( "Unable to open printcap file %s for read!\n", psz)); + return(False); + } + + for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line)) + { + if (*line == '#' || *line == 0) + continue; + + /* now we have a real printer line - cut it off at the first : */ + p = strchr(line,':'); + if (p) *p = 0; + + /* now just check if the name is in the list */ + /* NOTE: I avoid strtok as the fn calling this one may be using it */ + for (p=line; p; p=q) + { + if ((q = strchr(p,'|'))) *q++ = 0; + + if (strequal(p,pszPrintername)) + { + /* normalise the case */ + strcpy(pszPrintername,p); + free(line); + fclose(pfile); + return(True); + } + p = q; + } + } + + + fclose(pfile); + return(False); +} + + +/*************************************************************************** +run a function on each printer name in the printcap file. The function is +passed the primary name and the comment (if possible) +***************************************************************************/ +void pcap_printer_fn(void (*fn)()) +{ + pstring name,comment; + char *line; + char *psz; + char *p,*q; + FILE *pfile; + + /* only go looking if no printcap name supplied */ + if (((psz = lp_printcapname()) == NULL) || (psz[0] == '\0')) + { + DEBUG(0,( "No printcap file name configured!\n")); + return; + } + +#ifdef AIX + if (strlocate(psz,"/qconfig") != NULL) + { + ScanQconfig_fn(psz,fn); + return; + } +#endif + if ((pfile = fopen(psz, "r")) == NULL) + { + DEBUG(0,( "Unable to open printcap file %s for read!\n", psz)); + return; + } + + for (;(line = fgets_slash(NULL,sizeof(pstring),pfile)); free(line)) + { + if (*line == '#' || *line == 0) + continue; + + /* now we have a real printer line - cut it off at the first : */ + p = strchr(line,':'); + if (p) *p = 0; + + /* now find the most likely printer name and comment + this is pure guesswork, but it's better than nothing */ + *name = 0; + *comment = 0; + for (p=line; p; p=q) + { + BOOL has_punctuation; + if ((q = strchr(p,'|'))) *q++ = 0; + + has_punctuation = (strchr(p,' ') || strchr(p,'(') || strchr(p,')')); + + if (strlen(p)>strlen(comment) && has_punctuation) + { + StrnCpy(comment,p,sizeof(comment)-1); + continue; + } + + if (strlen(p) <= 8 && strlen(p)>strlen(name) && !has_punctuation) + { + if (!*comment) strcpy(comment,name); + strcpy(name,p); + continue; + } + + if (!strchr(comment,' ') && + strlen(p) > strlen(comment)) + { + StrnCpy(comment,p,sizeof(comment)-1); + continue; + } + } + + comment[60] = 0; + name[8] = 0; + + if (*name) + fn(name,comment); + } + fclose(pfile); +} diff --git a/source3/printing/printing.c b/source3/printing/printing.c new file mode 100644 index 0000000000..1dd8921800 --- /dev/null +++ b/source3/printing/printing.c @@ -0,0 +1,859 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + printing routines + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +extern int DEBUGLEVEL; +extern connection_struct Connections[]; +extern files_struct Files[]; + +static BOOL * lpq_cache_reset=NULL; + +static int check_lpq_cache(int snum) { + static int lpq_caches=0; + + if (lpq_caches <= snum) { + BOOL * p; + p = (BOOL *) Realloc(lpq_cache_reset,(snum+1)*sizeof(BOOL)); + if (p) { + lpq_cache_reset=p; + lpq_caches = snum+1; + } + } + return lpq_caches; +} + +void lpq_reset(int snum) +{ + if (check_lpq_cache(snum) > snum) lpq_cache_reset[snum]=True; +} + + +/**************************************************************************** +Build the print command in the supplied buffer. This means getting the +print command for the service and inserting the printer name and the +print file name. Return NULL on error, else the passed buffer pointer. +****************************************************************************/ +static char *build_print_command(int cnum, char *command, char *syscmd, char *filename1) +{ + int snum = SNUM(cnum); + char *tstr; + pstring filename; + + /* get the print command for the service. */ + tstr = command; + if (!syscmd || !tstr) { + DEBUG(0,("No print command for service `%s'\n", SERVICE(snum))); + return (NULL); + } + + /* copy the command into the buffer for extensive meddling. */ + StrnCpy(syscmd, tstr, sizeof(pstring) - 1); + + /* look for "%s" in the string. If there is no %s, we cannot print. */ + if (!strstr(syscmd, "%s") && !strstr(syscmd, "%f")) { + DEBUG(2,("WARNING! No placeholder for the filename in the print command for service %s!\n", SERVICE(snum))); + } + + if (strstr(syscmd,"%s")) { + int iOffset = strstr(syscmd, "%s") - syscmd; + + /* construct the full path for the filename, shouldn't be necessary unless + the subshell causes a "cd" to be executed. + Only use the full path if there isn't a / preceding the %s */ + if (iOffset==0 || syscmd[iOffset-1] != '/') { + StrnCpy(filename,Connections[cnum].connectpath,sizeof(filename)-1); + trim_string(filename,"","/"); + strcat(filename,"/"); + strcat(filename,filename1); + } + else + strcpy(filename,filename1); + + string_sub(syscmd, "%s", filename); + } + + string_sub(syscmd, "%f", filename1); + + /* Does the service have a printername? If not, make a fake and empty */ + /* printer name. That way a %p is treated sanely if no printer */ + /* name was specified to replace it. This eventuality is logged. */ + tstr = PRINTERNAME(snum); + if (tstr == NULL || tstr[0] == '\0') { + DEBUG(3,( "No printer name - using %s.\n", SERVICE(snum))); + tstr = SERVICE(snum); + } + + string_sub(syscmd, "%p", tstr); + + standard_sub(cnum,syscmd); + + return (syscmd); +} + + +/**************************************************************************** +print a file - called on closing the file +****************************************************************************/ +void print_file(int fnum) +{ + pstring syscmd; + int cnum = Files[fnum].cnum; + int snum=SNUM(cnum); + char *tempstr; + + *syscmd = 0; + + if (file_size(Files[fnum].name) <= 0) { + DEBUG(3,("Discarding null print job %s\n",Files[fnum].name)); + sys_unlink(Files[fnum].name); + return; + } + + tempstr = build_print_command(cnum, PRINTCOMMAND(snum), syscmd, Files[fnum].name); + if (tempstr != NULL) + { + int ret = smbrun(syscmd,NULL); + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + } + else + DEBUG(0,("Null print command?\n")); + + lpq_reset(snum); +} + +static char *Months[13] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Err"}; + + +/******************************************************************* +process time fields +********************************************************************/ +static time_t EntryTime(string tok[], int ptr, int count, int minimum) +{ + time_t jobtime; + + jobtime = time(NULL); /* default case: take current time */ + if (count >= minimum) { + struct tm *t; + int i, day, hour, min, sec; + char *c; + + for (i=0; i<13; i++) if (!strncmp(tok[ptr], Months[i],3)) break; /* Find month */ + if (i<12) { + t = localtime(&jobtime); + day = atoi(tok[ptr+1]); + c=(char *)(tok[ptr+2]); + *(c+2)=0; + hour = atoi(c); + *(c+5)=0; + min = atoi(c+3); + if(*(c+6) != 0)sec = atoi(c+6); + else sec=0; + + if ((t->tm_mon < i)|| + ((t->tm_mon == i)&& + ((t->tm_mday < day)|| + ((t->tm_mday == day)&& + (t->tm_hour*60+t->tm_min < hour*60+min))))) + t->tm_year--; /* last year's print job */ + + t->tm_mon = i; + t->tm_mday = day; + t->tm_hour = hour; + t->tm_min = min; + t->tm_sec = sec; + jobtime = mktime(t); + } + } + return jobtime; +} + + +/**************************************************************************** +parse a lpq line + +here is an example of lpq output under bsd + +Warning: no daemon present +Rank Owner Job Files Total Size +1st tridge 148 README 8096 bytes + +here is an example of lpq output under osf/1 + +Warning: no daemon present +Rank Pri Owner Job Files Total Size +1st 0 tridge 148 README 8096 bytes +****************************************************************************/ +static BOOL parse_lpq_bsd(char *line,print_queue_struct *buf,BOOL first) +{ +#ifdef OSF1 +#define RANKTOK 0 +#define PRIOTOK 1 +#define USERTOK 2 +#define JOBTOK 3 +#define FILETOK 4 +#define TOTALTOK 5 +#define NTOK 6 +#else /* OSF1 */ +#define RANKTOK 0 +#define USERTOK 1 +#define JOBTOK 2 +#define FILETOK 3 +#define TOTALTOK 4 +#define NTOK 5 +#endif /* OSF1 */ + + string tok[NTOK]; + int count=0; + +#ifdef OSF1 + int length; + length = strlen(line); + if (line[length-3] == ':') + return(False); +#endif /* OSF1 */ + + /* handle the case of "(standard input)" as a filename */ + string_sub(line,"standard input","STDIN"); + string_sub(line,"(","\""); + string_sub(line,")","\""); + + for (count=0; count<NTOK && next_token(&line,tok[count],NULL); count++) ; + + /* we must get NTOK tokens */ + if (count < NTOK) + return(False); + + /* the Job and Total columns must be integer */ + if (!isdigit(*tok[JOBTOK]) || !isdigit(*tok[TOTALTOK])) return(False); + + /* if the fname contains a space then use STDIN */ + if (strchr(tok[FILETOK],' ')) + strcpy(tok[FILETOK],"STDIN"); + + /* only take the last part of the filename */ + { + string tmp; + char *p = strrchr(tok[FILETOK],'/'); + if (p) + { + strcpy(tmp,p+1); + strcpy(tok[FILETOK],tmp); + } + } + + + buf->job = atoi(tok[JOBTOK]); + buf->size = atoi(tok[TOTALTOK]); + buf->status = strequal(tok[RANKTOK],"active")?LPQ_PRINTING:LPQ_QUEUED; + buf->time = time(NULL); + StrnCpy(buf->user,tok[USERTOK],sizeof(buf->user)-1); + StrnCpy(buf->file,tok[FILETOK],sizeof(buf->file)-1); +#ifdef PRIOTOK + buf->priority = atoi(tok[PRIOTOK]); +#else + buf->priority = 1; +#endif + return(True); +} + + + +/******************************************************************* +parse lpq on an aix system + +Queue Dev Status Job Files User PP % Blks Cp Rnk +------- ----- --------- --- ------------------ ---------- ---- -- ----- --- --- +lazer lazer READY +lazer lazer RUNNING 537 6297doc.A kvintus@IE 0 10 2445 1 1 + QUEUED 538 C.ps root@IEDVB 124 1 2 + QUEUED 539 E.ps root@IEDVB 28 1 3 + QUEUED 540 L.ps root@IEDVB 172 1 4 + QUEUED 541 P.ps root@IEDVB 22 1 5 +********************************************************************/ +static BOOL parse_lpq_aix(char *line,print_queue_struct *buf,BOOL first) +{ + string tok[11]; + int count=0; + + /* handle the case of "(standard input)" as a filename */ + string_sub(line,"standard input","STDIN"); + string_sub(line,"(","\""); + string_sub(line,")","\""); + + for (count=0; count<10 && next_token(&line,tok[count],NULL); count++) ; + + /* we must get 6 tokens */ + if (count < 10) + { + if ((count == 7) && (strcmp(tok[0],"QUEUED") == 0)) + { + /* the 2nd and 5th columns must be integer */ + if (!isdigit(*tok[1]) || !isdigit(*tok[4])) return(False); + buf->size = atoi(tok[4]) * 1024; + /* if the fname contains a space then use STDIN */ + if (strchr(tok[2],' ')) + strcpy(tok[2],"STDIN"); + + /* only take the last part of the filename */ + { + string tmp; + char *p = strrchr(tok[2],'/'); + if (p) + { + strcpy(tmp,p+1); + strcpy(tok[2],tmp); + } + } + + + buf->job = atoi(tok[1]); + buf->status = LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->user,tok[3],sizeof(buf->user)-1); + StrnCpy(buf->file,tok[2],sizeof(buf->file)-1); + } + else + { + DEBUG(6,("parse_lpq_aix count=%d\n", count)); + return(False); + } + } + else + { + /* the 4th and 9th columns must be integer */ + if (!isdigit(*tok[3]) || !isdigit(*tok[8])) return(False); + buf->size = atoi(tok[8]) * 1024; + /* if the fname contains a space then use STDIN */ + if (strchr(tok[4],' ')) + strcpy(tok[4],"STDIN"); + + /* only take the last part of the filename */ + { + string tmp; + char *p = strrchr(tok[4],'/'); + if (p) + { + strcpy(tmp,p+1); + strcpy(tok[4],tmp); + } + } + + + buf->job = atoi(tok[3]); + buf->status = strequal(tok[2],"RUNNING")?LPQ_PRINTING:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->user,tok[5],sizeof(buf->user)-1); + StrnCpy(buf->file,tok[4],sizeof(buf->file)-1); + } + + + return(True); +} + + +/**************************************************************************** +parse a lpq line +here is an example of lpq output under hpux; note there's no space after -o ! +$> lpstat -oljplus +ljplus-2153 user priority 0 Jan 19 08:14 on ljplus + util.c 125697 bytes + server.c 110712 bytes +ljplus-2154 user priority 0 Jan 19 08:14 from client + (standard input) 7551 bytes +****************************************************************************/ +static BOOL parse_lpq_hpux(char * line, print_queue_struct *buf, BOOL first) +{ + /* must read two lines to process, therefore keep some values static */ + static BOOL header_line_ok=False, base_prio_reset=False; + static string jobuser; + static int jobid; + static int jobprio; + static time_t jobtime; + static int jobstat=LPQ_QUEUED; + /* to store minimum priority to print, lpstat command should be invoked + with -p option first, to work */ + static int base_prio; + + int count; + char TAB = '\011'; + string tok[12]; + + /* If a line begins with a horizontal TAB, it is a subline type */ + + if (line[0] == TAB) { /* subline */ + /* check if it contains the base priority */ + if (!strncmp(line,"\tfence priority : ",18)) { + base_prio=atoi(&line[18]); + DEBUG(4, ("fence priority set at %d\n", base_prio)); + } + if (!header_line_ok) return (False); /* incorrect header line */ + /* handle the case of "(standard input)" as a filename */ + string_sub(line,"standard input","STDIN"); + string_sub(line,"(","\""); + string_sub(line,")","\""); + + for (count=0; count<2 && next_token(&line,tok[count],NULL); count++) ; + /* we must get 2 tokens */ + if (count < 2) return(False); + + /* the 2nd column must be integer */ + if (!isdigit(*tok[1])) return(False); + + /* if the fname contains a space then use STDIN */ + if (strchr(tok[0],' ')) + strcpy(tok[0],"STDIN"); + + buf->size = atoi(tok[1]); + StrnCpy(buf->file,tok[0],sizeof(buf->file)-1); + + /* fill things from header line */ + buf->time = jobtime; + buf->job = jobid; + buf->status = jobstat; + buf->priority = jobprio; + StrnCpy(buf->user,jobuser,sizeof(buf->user)-1); + + return(True); + } + else { /* header line */ + header_line_ok=False; /* reset it */ + if (first) { + if (!base_prio_reset) { + base_prio=0; /* reset it */ + base_prio_reset=True; + } + } + else if (base_prio) base_prio_reset=False; + + /* handle the dash in the job id */ + string_sub(line,"-"," "); + + for (count=0; count<12 && next_token(&line,tok[count],NULL); count++) ; + + /* we must get 8 tokens */ + if (count < 8) return(False); + + /* first token must be printer name (cannot check ?) */ + /* the 2nd, 5th & 7th column must be integer */ + if (!isdigit(*tok[1]) || !isdigit(*tok[4]) || !isdigit(*tok[6])) return(False); + jobid = atoi(tok[1]); + StrnCpy(jobuser,tok[2],sizeof(buf->user)-1); + jobprio = atoi(tok[4]); + + /* process time */ + jobtime=EntryTime(tok, 5, count, 8); + if (jobprio < base_prio) { + jobstat = LPQ_PAUSED; + DEBUG (4, ("job %d is paused: prio %d < %d; jobstat=%d\n", jobid, jobprio, base_prio, jobstat)); + } + else { + jobstat = LPQ_QUEUED; + if ((count >8) && (((strequal(tok[8],"on")) || + ((strequal(tok[8],"from")) && + ((count > 10)&&(strequal(tok[10],"on"))))))) + jobstat = LPQ_PRINTING; + } + + header_line_ok=True; /* information is correct */ + return(False); /* need subline info to include into queuelist */ + } +} + + +/**************************************************************************** +parse a lpq line + +here is an example of "lpstat -o dcslw" output under sysv + +dcslw-896 tridge 4712 Dec 20 10:30:30 on dcslw +dcslw-897 tridge 4712 Dec 20 10:30:30 being held + +****************************************************************************/ +static BOOL parse_lpq_sysv(char *line,print_queue_struct *buf,BOOL first) +{ + string tok[9]; + int count=0; + char *p; + + /* handle the dash in the job id */ + string_sub(line,"-"," "); + + for (count=0; count<9 && next_token(&line,tok[count],NULL); count++) ; + + /* we must get 7 tokens */ + if (count < 7) + return(False); + + /* the 2nd and 4th, 6th columns must be integer */ + if (!isdigit(*tok[1]) || !isdigit(*tok[3])) return(False); + if (!isdigit(*tok[5])) return(False); + + /* if the user contains a ! then trim the first part of it */ + if ((p=strchr(tok[2],'!'))) + { + string tmp; + strcpy(tmp,p+1); + strcpy(tok[2],tmp); + } + + + buf->job = atoi(tok[1]); + buf->size = atoi(tok[3]); + if (count > 7 && strequal(tok[7],"on")) + buf->status = LPQ_PRINTING; + else if (count > 8 && strequal(tok[7],"being") && strequal(tok[8],"held")) + buf->status = LPQ_PAUSED; + else + buf->status = LPQ_QUEUED; + buf->priority = 0; + buf->time = EntryTime(tok, 4, count, 7); + StrnCpy(buf->user,tok[2],sizeof(buf->user)-1); + StrnCpy(buf->file,tok[2],sizeof(buf->file)-1); + return(True); +} + +/**************************************************************************** +parse a lpq line + +here is an example of lpq output under qnx +Spooler: /qnx/spooler, on node 1 +Printer: txt (ready) +0000: root [job #1 ] active 1146 bytes /etc/profile +0001: root [job #2 ] ready 2378 bytes /etc/install +0002: root [job #3 ] ready 1146 bytes -- standard input -- +****************************************************************************/ +static BOOL parse_lpq_qnx(char *line,print_queue_struct *buf,BOOL first) +{ + string tok[7]; + int count=0; + + DEBUG(0,("antes [%s]\n", line)); + + /* handle the case of "-- standard input --" as a filename */ + string_sub(line,"standard input","STDIN"); + DEBUG(0,("despues [%s]\n", line)); + string_sub(line,"-- ","\""); + string_sub(line," --","\""); + DEBUG(0,("despues 1 [%s]\n", line)); + + string_sub(line,"[job #",""); + string_sub(line,"]",""); + DEBUG(0,("despues 2 [%s]\n", line)); + + + + for (count=0; count<7 && next_token(&line,tok[count],NULL); count++) ; + + /* we must get 7 tokens */ + if (count < 7) + return(False); + + /* the 3rd and 5th columns must be integer */ + if (!isdigit(*tok[2]) || !isdigit(*tok[4])) return(False); + + /* only take the last part of the filename */ + { + string tmp; + char *p = strrchr(tok[6],'/'); + if (p) + { + strcpy(tmp,p+1); + strcpy(tok[6],tmp); + } + } + + + buf->job = atoi(tok[2]); + buf->size = atoi(tok[4]); + buf->status = strequal(tok[3],"active")?LPQ_PRINTING:LPQ_QUEUED; + buf->priority = 0; + buf->time = time(NULL); + StrnCpy(buf->user,tok[1],sizeof(buf->user)-1); + StrnCpy(buf->file,tok[6],sizeof(buf->file)-1); + return(True); +} + + + +char *stat0_strings[] = { "enabled", "online", "idle", "no entries", "free", "ready", NULL }; +char *stat1_strings[] = { "offline", "disabled", "down", "off", "waiting", "no daemon", NULL }; +char *stat2_strings[] = { "jam", "paper", "error", "responding", "not accepting", "not running", "turned off", NULL }; + +/**************************************************************************** +parse a lpq line. Choose printing style +****************************************************************************/ +static BOOL parse_lpq_entry(int snum,char *line, + print_queue_struct *buf, + print_status_struct *status,BOOL first) +{ + BOOL ret; + + switch (lp_printing()) + { + case PRINT_SYSV: + ret = parse_lpq_sysv(line,buf,first); + break; + case PRINT_AIX: + ret = parse_lpq_aix(line,buf,first); + break; + case PRINT_HPUX: + ret = parse_lpq_hpux(line,buf,first); + break; + case PRINT_QNX: + ret = parse_lpq_qnx(line,buf,first); + break; + default: + ret = parse_lpq_bsd(line,buf,first); + break; + } + +#ifdef LPQ_GUEST_TO_USER + if (ret) { + extern pstring sesssetup_user; + /* change guest entries to the current logged in user to make + them appear deletable to windows */ + if (sesssetup_user[0] && strequal(buf->user,lp_guestaccount(snum))) + strcpy(buf->user,sesssetup_user); + } +#endif + + if (status && !ret) + { + /* a few simple checks to see if the line might be a + printer status line: + handle them so that most severe condition is shown */ + int i; + strlower(line); + + switch (status->status) { + case LPSTAT_OK: + for (i=0; stat0_strings[i]; i++) + if (strstr(line,stat0_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_OK; + } + case LPSTAT_STOPPED: + for (i=0; stat1_strings[i]; i++) + if (strstr(line,stat1_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_STOPPED; + } + case LPSTAT_ERROR: + for (i=0; stat2_strings[i]; i++) + if (strstr(line,stat2_strings[i])) { + StrnCpy(status->message,line,sizeof(status->message)-1); + status->status=LPSTAT_ERROR; + } + break; + } + } + + return(ret); +} + +/**************************************************************************** +get a printer queue +****************************************************************************/ +int get_printqueue(int snum,int cnum,print_queue_struct **queue, + print_status_struct *status) +{ + char *lpq_command = lp_lpqcommand(snum); + char *printername = PRINTERNAME(snum); + int ret=0,count=0; + pstring syscmd; + fstring outfile; + pstring line; + FILE *f; + struct stat sbuf; + BOOL dorun=True; + int cachetime = lp_lpqcachetime(); + int lfd = -1; + + *line = 0; + check_lpq_cache(snum); + + if (!printername || !*printername) + { + DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", + lp_servicename(snum),snum)); + printername = lp_servicename(snum); + } + + if (!lpq_command || !(*lpq_command)) + { + DEBUG(5,("No lpq command\n")); + return(0); + } + + strcpy(syscmd,lpq_command); + string_sub(syscmd,"%p",printername); + + standard_sub(cnum,syscmd); + + sprintf(outfile,"/tmp/lpq.%08x",str_checksum(syscmd)); + + if (!lpq_cache_reset[snum] && cachetime && !stat(outfile,&sbuf)) + { + if (time(NULL) - sbuf.st_mtime < cachetime) { + DEBUG(3,("Using cached lpq output\n")); + dorun = False; + } + + if (dorun) { + lfd = file_lock(outfile,LPQ_LOCK_TIMEOUT); + if (lfd<0 || + (!fstat(lfd,&sbuf) && (time(NULL) - sbuf.st_mtime)<cachetime)) { + DEBUG(3,("Using cached lpq output\n")); + dorun = False; + file_unlock(lfd); lfd = -1; + } + } + } + + if (dorun) { + ret = smbrun(syscmd,outfile); + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + } + + lpq_cache_reset[snum] = False; + + f = fopen(outfile,"r"); + if (!f) { + if (lfd >= 0) file_unlock(lfd); + return(0); + } + + if (status) { + strcpy(status->message,""); + status->status = LPSTAT_OK; + } + + while (fgets(line,sizeof(pstring),f)) + { + DEBUG(6,("QUEUE2: %s\n",line)); + + *queue = Realloc(*queue,sizeof(print_queue_struct)*(count+1)); + if (! *queue) + { + count = 0; + break; + } + + bzero((char *)&(*queue)[count],sizeof(**queue)); + + /* parse it */ + if (!parse_lpq_entry(snum,line,&(*queue)[count],status,count==0)) + continue; + + count++; + } + + fclose(f); + + if (lfd >= 0) file_unlock(lfd); + + if (!cachetime) + unlink(outfile); + else + chmod(outfile,0666); + return(count); +} + + +/**************************************************************************** +delete a printer queue entry +****************************************************************************/ +void del_printqueue(int cnum,int snum,int jobid) +{ + char *lprm_command = lp_lprmcommand(snum); + char *printername = PRINTERNAME(snum); + pstring syscmd; + char jobstr[20]; + int ret; + + if (!printername || !*printername) + { + DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", + lp_servicename(snum),snum)); + printername = lp_servicename(snum); + } + + if (!lprm_command || !(*lprm_command)) + { + DEBUG(5,("No lprm command\n")); + return; + } + + sprintf(jobstr,"%d",jobid); + + strcpy(syscmd,lprm_command); + string_sub(syscmd,"%p",printername); + string_sub(syscmd,"%j",jobstr); + standard_sub(cnum,syscmd); + + ret = smbrun(syscmd,NULL); + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + lpq_reset(snum); /* queue has changed */ +} + +/**************************************************************************** +change status of a printer queue entry +****************************************************************************/ +void status_printjob(int cnum,int snum,int jobid,int status) +{ + char *lpstatus_command = + (status==LPQ_PAUSED?lp_lppausecommand(snum):lp_lpresumecommand(snum)); + char *printername = PRINTERNAME(snum); + pstring syscmd; + char jobstr[20]; + int ret; + + if (!printername || !*printername) + { + DEBUG(6,("replacing printer name with service (snum=(%s,%d))\n", + lp_servicename(snum),snum)); + printername = lp_servicename(snum); + } + + if (!lpstatus_command || !(*lpstatus_command)) + { + DEBUG(5,("No lpstatus command to %s job\n", + (status==LPQ_PAUSED?"pause":"resume"))); + return; + } + + sprintf(jobstr,"%d",jobid); + + strcpy(syscmd,lpstatus_command); + string_sub(syscmd,"%p",printername); + string_sub(syscmd,"%j",jobstr); + standard_sub(cnum,syscmd); + + ret = smbrun(syscmd,NULL); + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + lpq_reset(snum); /* queue has changed */ +} + + diff --git a/source3/script/addtosmbpass b/source3/script/addtosmbpass new file mode 100644 index 0000000000..42af518397 --- /dev/null +++ b/source3/script/addtosmbpass @@ -0,0 +1,74 @@ +#!/usr/bin/awk -f +# edit the line above to point to your real location of awk interpreter + +# awk program for adding new entries in smbpasswd files +# arguments are account names to add; feed it an existent Samba password +# file on stdin, results will be written on stdout +# +# Michal Jaegermann, michal@ellpspace.math.ualberta.ca, 1995-11-09 + +BEGIN { + me = "addtosmbpass"; + count = ARGC; + FS = ":"; + + if (count == 1) { + print "Usage:", me, + "name1 [name2 ....] < smbpasswd.in > smbpasswd.out"; + ARGV[1] = "/dev/null"; + ARGC = 2; + exit; + } + + for(i = 1; i < count; i++) { + names[ARGV[i]] = " "; + delete ARGV[i]; + } +# sane awk should work simply with 'ARGC = 1', but not every awk +# implementation is sane - big sigh!! + ARGV[1] = "-"; + ARGC = 2; +# +# If you have ypmatch but is not RPC registered (some Linux systems +# for example) comment out the next line. +# "which ypmatch" | getline ypmatch; + if (1 != match(ypmatch, /^\//)) { + ypmatch = ""; + } + pwdf = "/etc/passwd"; +} +#check for names already present in input +{ + print $0; + for(name in names) { + if($1 ~ name) { + delete names[name]; + } + } +} +END { + fmt = "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:"; + fmt = fmt "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n"; + for(name in names) { + while ((getline < pwdf) > 0) { + if ($1 == name) { + printf(fmt, $1, $3, $5, $6, $7); + close(pwdf); + notfound = ""; + break; + } + notfound = "n"; + } + $0 = ""; + if (notfound && ypmatch) { +# try to find in NIS databases + command = ypmatch " " name " passwd"; + command | getline; + if (NF > 0) { + printf(fmt, $1, $3, $5, $6, $7); + } + close(command); + } + } +} + diff --git a/source3/script/installbin.sh b/source3/script/installbin.sh new file mode 100755 index 0000000000..633e6cb5bb --- /dev/null +++ b/source3/script/installbin.sh @@ -0,0 +1,42 @@ +#!/bin/sh +INSTALLPERMS=$1 +BASEDIR=$2 +BINDIR=$3 +LIBDIR=$4 +VARDIR=$5 +shift +shift +shift +shift +shift + +for d in $BASEDIR $BINDIR $LIBDIR $VARDIR; do +if [ ! -d $d ]; then +mkdir $d +if [ ! -d $d ]; then + echo Failed to make directory $d + exit 1 +fi +fi +done + + +for p in $*; do + echo Installing $p as $BINDIR/$p + if [ -f $BINDIR/$p ]; then + mv $BINDIR/$p $BINDIR/$p.old + fi + cp $p $BINDIR/$p + chmod $INSTALLPERMS $BINDIR/$p +done + + +cat << EOF +====================================================================== +The binaries are installed. You may restore the old binaries (if there +were any) using the command "make revert" +====================================================================== +EOF + +exit 0 + diff --git a/source3/script/installman.sh b/source3/script/installman.sh new file mode 100755 index 0000000000..a79d157c5f --- /dev/null +++ b/source3/script/installman.sh @@ -0,0 +1,35 @@ +#!/bin/sh +MANDIR=$1 +SRCDIR=$2 + +echo Installing man pages in $MANDIR + +for d in $MANDIR $MANDIR/man1 $MANDIR/man5 $MANDIR/man7 $MANDIR/man8; do +if [ ! -d $d ]; then +mkdir $d +if [ ! -d $d ]; then + echo Failed to make directory $d + exit 1 +fi +fi +done + +cp $SRCDIR../docs/*.1 $MANDIR/man1 +cp $SRCDIR../docs/*.5 $MANDIR/man5 +cp $SRCDIR../docs/*.8 $MANDIR/man8 +cp $SRCDIR../docs/*.7 $MANDIR/man7 +echo Setting permissions on man pages +chmod 0644 $MANDIR/man1/smbstatus.1 +chmod 0644 $MANDIR/man1/smbclient.1 +chmod 0644 $MANDIR/man1/smbrun.1 +chmod 0644 $MANDIR/man1/testparm.1 +chmod 0644 $MANDIR/man1/testprns.1 +chmod 0644 $MANDIR/man1/smbtar.1 +chmod 0644 $MANDIR/man5/smb.conf.5 +chmod 0644 $MANDIR/man7/samba.7 +chmod 0644 $MANDIR/man8/smbd.8 +chmod 0644 $MANDIR/man8/nmbd.8 + +echo Man pages installed +exit 0 + diff --git a/source3/script/mksmbpasswd.sh b/source3/script/mksmbpasswd.sh new file mode 100755 index 0000000000..6e592acd65 --- /dev/null +++ b/source3/script/mksmbpasswd.sh @@ -0,0 +1,6 @@ +#!/bin/sh +awk 'BEGIN {FS=":" + printf("#\n# SMB password file.\n#\n") + } +{ printf( "%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:%s:%s:%s\n", $1, $3, $5, $6, $7) } +' diff --git a/source3/script/revert.sh b/source3/script/revert.sh new file mode 100755 index 0000000000..68b47bf39d --- /dev/null +++ b/source3/script/revert.sh @@ -0,0 +1,15 @@ +#!/bin/sh +BINDIR=$1 +shift + +for p in $*; do + if [ -f $BINDIR/$p.old ]; then + echo Restoring $BINDIR/$p.old as $BINDIR/$p + mv $BINDIR/$p $BINDIR/$p.new + mv $BINDIR/$p.old $BINDIR/$p + rm -f $BINDIR/$p.new + fi +done + +exit 0 + diff --git a/source3/script/smbtar b/source3/script/smbtar new file mode 100644 index 0000000000..fc032ed41c --- /dev/null +++ b/source3/script/smbtar @@ -0,0 +1,141 @@ +#!/bin/sh +# +# smbtar script - front end to smbclient +# +# Authors: Martin.Kraemer <Martin.Kraemer@mch.sni.de> +# and Ricky Poulten (ricky@logcam.co.uk) +# +# (May need to change shell to ksh for HPUX or OSF for better getopts) + +case $0 in + # when called by absolute path, assume smbclient is in the same directory + /*) + SMBCLIENT="`dirname $0`/smbclient";; + *) # edit this to show where your smbclient is + SMBCLIENT="./smbclient";; +esac + +# These are the default values. You could fill them in if you know what +# you're doing, but beware: better not store a plain text password! +server="" +service="backup" # Default: a service called "backup" +password="" +username=$LOGNAME # Default: same user name as in *nix +verbose="2>/dev/null" # Default: no echo to stdout +log="-d 2" +newer="" +blocksize="" +tarcmd="c" +tarargs="" +cdcmd="\\" +tapefile=${TAPE-tar.out} + +Usage(){ + ex=$1 + shift +echo >&2 "Usage: `basename $0` [<options>] [<include/exclude files>] +Function: backup/restore a Windows PC directories to a local tape file +Options: (Description) (Default) + -r Restore from tape file to PC Save from PC to tapefile + -i Incremental mode Full backup mode + -v Verbose mode: echo command Don't echo anything + -s <server> Specify PC Server $server + -p <password> Specify PC Password $password + -x <share> Specify PC Share $service + -X Exclude mode Include + -N <newer> File for date comparison `set -- $newer; echo $2` + -b <blocksize> Specify tape's blocksize `set -- $blocksize; echo $2` + -d <dir> Specify a directory in share $cdcmd + -l <log> Specify a Samba Log Level `set -- $log; echo $2` + -u <user> Specify User Name $username + -t <tape> Specify Tape device $tapefile +" + echo >&2 "$@" + exit $ex +} + +while getopts rivl:b:d:N:s:p:x:u:Xt: c; do + case $c in + r) # [r]estore to Windows (instead of the default "Save from Windows") + tarcmd="x" + ;; + i) # [i]ncremental + tarargs=${tarargs}g + ;; + l) # specify [l]og file + log="-d $OPTARG" + case "$OPTARG" in + [0-9]*) ;; + *) echo >&2 "$0: Error, log level not numeric: -l $OPTARG" + exit 1 + esac + ;; + d) # specify [d]irectory to change to in server's share + cdcmd="$OPTARG" + ;; + N) # compare with a file, test if [n]ewer + if [ -f $OPTARG ]; then + newer=$OPTARG + tarargs=${tarargs}N + else + echo >&2 $0: Warning, $OPTARG not found + fi + ;; + X) # Add exclude flag + tarargs=${tarargs}X + ;; + s) # specify [s]erver's share to connect to - this MUST be given. + server="$OPTARG" + ;; + b) # specify [b]locksize + blocksize="blocksize $OPTARG" + case "$OPTARG" in + [0-9]*) ;; + *) echo >&2 "$0: Error, block size not numeric: -b $OPTARG" + exit 1 + esac + tarargs=${tarargs}b + ;; + p) # specify [p]assword to use + password="$OPTARG" + ;; + x) # specify windows [s]hare to use + service="$OPTARG" + ;; + t) # specify [t]apefile on local host + tapefile="$OPTARG" + ;; + u) # specify [u]sername for connection + username="$OPTARG" + ;; + v) # be [v]erbose and display what's going on + verbose="" + ;; + '?') # any other switch + Usage 2 "Invalid switch specified - abort." + ;; + esac +done + +shift `expr $OPTIND - 1` + +if [ "$server" = "" ] || [ "$service" = "" ]; then + Usage 1 "No server or no service specified - abort." +fi + +# if the -v switch is set, the echo the current parameters +if [ -z "$verbose" ]; then + echo "server is $server" +# echo "share is $service" + echo "share is $service\\$cdcmd" + echo "tar args is $tarargs" +# echo "password is $password" # passwords should never be sent to screen + echo "tape is $tapefile" + echo "blocksize is $blocksize" +fi + +eval $SMBCLIENT "'\\\\$server\\$service'" "'$password'" -U "'$username'" \ +-E -N $log -D "'$cdcmd'" \ +-T${tarcmd}${tarargs} $blocksize $newer $tapefile $* $verbose + + diff --git a/source3/script/updatesmbpasswd.sh b/source3/script/updatesmbpasswd.sh new file mode 100755 index 0000000000..1d7e0d7332 --- /dev/null +++ b/source3/script/updatesmbpasswd.sh @@ -0,0 +1,14 @@ +#!/bin/sh +nawk 'BEGIN {FS=":"} +{ + if( $0 ~ "^#" ) { + print $0 + } else if( (length($4) == 32) && (($4 ~ "^[0-9A-F]*$") || ($4 ~ "^[X]*$") || ( $4 ~ "^[*]*$"))) { + print $0 + } else { + printf( "%s:%s:%s:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:", $1, $2, $3); + for(i = 4; i <= NF; i++) + printf("%s:", $i) + printf("\n") + } +}' diff --git a/source3/smbd/chgpasswd.c b/source3/smbd/chgpasswd.c new file mode 100644 index 0000000000..dc0514c1ed --- /dev/null +++ b/source3/smbd/chgpasswd.c @@ -0,0 +1,376 @@ +/* fork a child process to exec passwd and write to its +* tty to change a users password. This is running as the +* user who is attempting to change the password. +*/ + +/* + * This code was copied/borrowed and stolen from various sources. + * The primary source was the poppasswd.c from the authors of POPMail. This software + * was included as a client to change passwords using the 'passwd' program + * on the remote machine. + * + * This routine is called by set_user_password() in password.c only if ALLOW_PASSWORD_CHANGE + * is defined in the compiler directives located in the Makefile. + * + * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson + * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences + * and rights to modify, distribute or incorporate this change to the CAP suite or + * using it for any other reason are granted, so long as this disclaimer is left intact. + */ + +/* + This code was hacked considerably for inclusion in Samba, primarily + by Andrew.Tridgell@anu.edu.au. The biggest change was the addition + of the "password chat" option, which allows the easy runtime + specification of the expected sequence of events to change a + password. + */ + +#include "includes.h" +#include "loadparm.h" + +extern int DEBUGLEVEL; + +#ifdef ALLOW_CHANGE_PASSWORD + +#define MINPASSWDLENGTH 5 +#define BUFSIZE 512 + +static int findpty(char **slave) +{ + int master; +#ifdef SVR4 + extern char *ptsname(); +#else + static char line[12] = "/dev/ptyXX"; + void *dirp; + char *dpname; +#endif + +#ifdef SVR4 + if ((master = open("/dev/ptmx", O_RDWR)) >= 1) { + grantpt(master); + unlockpt(master); + *slave = ptsname(master); + return (master); + } +#else + dirp = OpenDir("/dev"); + if (!dirp) return(-1); + while ((dpname = ReadDirName(dirp)) != NULL) { + if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) { + line[8] = dpname[3]; + line[9] = dpname[4]; + if ((master = open(line, O_RDWR)) >= 0) { + line[5] = 't'; + *slave = line; + CloseDir(dirp); + return (master); + } + } + } + CloseDir(dirp); +#endif + return (-1); +} + +static int dochild(int master,char *slavedev, char *name, char *passwordprogram) +{ + int slave; + struct termios stermios; + struct passwd *pass = Get_Pwnam(name,True); + int gid = pass->pw_gid; + int uid = pass->pw_uid; + +#ifdef USE_SETRES + setresuid(0,0,0); +#else + setuid(0); +#endif + + /* Start new session - gets rid of controlling terminal. */ + if (setsid() < 0) { + DEBUG(3,("Weirdness, couldn't let go of controlling terminal\n")); + return(False); + } + + /* Open slave pty and acquire as new controlling terminal. */ + if ((slave = open(slavedev, O_RDWR)) < 0) { + DEBUG(3,("More weirdness, could not open %s\n", + slavedev)); + return(False); + } +#ifdef SVR4 + ioctl(slave, I_PUSH, "ptem"); + ioctl(slave, I_PUSH, "ldterm"); +#else + if (ioctl(slave,TIOCSCTTY,0) <0) { + DEBUG(3,("Error in ioctl call for slave pty\n")); + /* return(False); */ + } +#endif + + /* Close master. */ + close(master); + + /* Make slave stdin/out/err of child. */ + + if (dup2(slave, STDIN_FILENO) != STDIN_FILENO) { + DEBUG(3,("Could not re-direct stdin\n")); + return(False); + } + if (dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) { + DEBUG(3,("Could not re-direct stdout\n")); + return(False); + } + if (dup2(slave, STDERR_FILENO) != STDERR_FILENO) { + DEBUG(3,("Could not re-direct stderr\n")); + return(False); + } + if (slave > 2) close(slave); + + /* Set proper terminal attributes - no echo, canonical input processing, + no map NL to CR/NL on output. */ + + if (tcgetattr(0, &stermios) < 0) { + DEBUG(3,("could not read default terminal attributes on pty\n")); + return(False); + } + stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + stermios.c_lflag |= ICANON; + stermios.c_oflag &= ~(ONLCR); + if (tcsetattr(0, TCSANOW, &stermios) < 0) { + DEBUG(3,("could not set attributes of pty\n")); + return(False); + } + + /* make us completely into the right uid */ +#ifdef USE_SETRES + setresgid(0,0,0); + setresuid(0,0,0); + setresgid(gid,gid,gid); + setresuid(uid,uid,uid); +#else + setuid(0); + seteuid(0); + setgid(gid); + setegid(gid); + setuid(uid); + seteuid(uid); +#endif + + /* execl() password-change application */ + if (execl("/bin/sh","sh","-c",passwordprogram,NULL) < 0) { + DEBUG(3,("Bad status returned from %s\n",passwordprogram)); + return(False); + } + return(True); +} + +static int expect(int master,char *expected,char *buf) +{ + int n, m; + + n = 0; + buf[0] = 0; + while (1) { + if (n >= BUFSIZE-1) { + return False; + } + + /* allow 4 seconds for some output to appear */ + m = read_with_timeout(master, buf+n, 1, BUFSIZE-1-n, 4000, True); + if (m < 0) + return False; + + n += m; + buf[n] = 0; + + { + pstring s1,s2; + strcpy(s1,buf); + strcpy(s2,expected); + if (do_match(s1, s2, False)) + return(True); + } + } +} + +static void pwd_sub(char *buf) +{ + string_sub(buf,"\\n","\n"); + string_sub(buf,"\\r","\r"); + string_sub(buf,"\\s"," "); + string_sub(buf,"\\t","\t"); +} + +static void writestring(int fd,char *s) +{ + int l; + + l = strlen (s); + write (fd, s, l); +} + + +static int talktochild(int master, char *chatsequence) +{ + char buf[BUFSIZE]; + int count=0; + char *ptr=chatsequence; + fstring chatbuf; + + *buf = 0; + sleep(1); + + while (next_token(&ptr,chatbuf,NULL)) { + BOOL ok=True; + count++; + pwd_sub(chatbuf); + if (!strequal(chatbuf,".")) + ok = expect(master,chatbuf,buf); + +#if DEBUG_PASSWORD + DEBUG(100,("chatbuf=[%s] responsebuf=[%s]\n",chatbuf,buf)); +#endif + + if (!ok) { + DEBUG(3,("response %d incorrect\n",count)); + return(False); + } + + if (!next_token(&ptr,chatbuf,NULL)) break; + pwd_sub(chatbuf); + if (!strequal(chatbuf,".")) + writestring(master,chatbuf); + +#if DEBUG_PASSWORD + DEBUG(100,("sendbuf=[%s]\n",chatbuf)); +#endif + } + + if (count<1) return(False); + + return (True); +} + + +BOOL chat_with_program(char *passwordprogram,char *name,char *chatsequence) +{ + char *slavedev; + int master; + pid_t pid, wpid; + int wstat; + BOOL chstat; + + /* allocate a pseudo-terminal device */ + if ((master = findpty (&slavedev)) < 0) { + DEBUG(3,("Cannot Allocate pty for password change: %s",name)); + return(False); + } + + if ((pid = fork()) < 0) { + DEBUG(3,("Cannot fork() child for password change: %s",name)); + return(False); + } + + /* we now have a pty */ + if (pid > 0){ /* This is the parent process */ + if ((chstat = talktochild(master, chatsequence)) == False) { + DEBUG(3,("Child failed to change password: %s\n",name)); + kill(pid, SIGKILL); /* be sure to end this process */ + return(False); + } + if ((wpid = waitpid(pid, &wstat, 0)) < 0) { + DEBUG(3,("The process is no longer waiting!\n\n")); + return(False); + } + if (pid != wpid) { + DEBUG(3,("We were waiting for the wrong process ID\n")); + return(False); + } + if (WIFEXITED(wstat) == 0) { + DEBUG(3,("The process exited while we were waiting\n")); + return(False); + } + if (WEXITSTATUS(wstat) != 0) { + DEBUG(3,("The status of the process exiting was %d\n", wstat)); + return(False); + } + + } else { + /* CHILD */ + + /* make sure it doesn't freeze */ + alarm(20); + + DEBUG(3,("Dochild for user %s (uid=%d,gid=%d)\n",name,getuid(),getgid())); + chstat = dochild(master, slavedev, name, passwordprogram); + } + DEBUG(3,("Password change %ssuccessful for user %s\n", (chstat?"":"un"), name)); + return (chstat); +} + + +BOOL chgpasswd(char *name,char *oldpass,char *newpass) +{ + pstring passwordprogram; + pstring chatsequence; + + strlower(name); + DEBUG(3,("Password change for user: %s\n",name)); + +#if DEBUG_PASSWORD + DEBUG(100,("Passwords: old=%s new=%s\n",oldpass,newpass)); +#endif + + /* Take the passed information and test it for minimum criteria */ + /* Minimum password length */ + if (strlen(newpass) < MINPASSWDLENGTH) /* too short, must be at least MINPASSWDLENGTH */ + { + DEBUG(2,("Password Change: %s, New password is shorter than MINPASSWDLENGTH\n",name)); + return (False); /* inform the user */ + } + + /* Password is same as old password */ + if (strcmp(oldpass,newpass) == 0) /* don't allow same password */ + { + DEBUG(2,("Password Change: %s, New password is same as old\n",name)); /* log the attempt */ + return (False); /* inform the user */ + } + +#if (defined(PASSWD_PROGRAM) && defined(PASSWD_CHAT)) + strcpy(passwordprogram,PASSWD_PROGRAM); + strcpy(chatsequence,PASSWD_CHAT); +#else + strcpy(passwordprogram,lp_passwd_program()); + strcpy(chatsequence,lp_passwd_chat()); +#endif + + if (!*chatsequence) { + DEBUG(2,("Null chat sequence - no password changing\n")); + return(False); + } + + if (!*passwordprogram) { + DEBUG(2,("Null password program - no password changing\n")); + return(False); + } + + string_sub(passwordprogram,"%u",name); + string_sub(passwordprogram,"%o",oldpass); + string_sub(passwordprogram,"%n",newpass); + + string_sub(chatsequence,"%u",name); + string_sub(chatsequence,"%o",oldpass); + string_sub(chatsequence,"%n",newpass); + return(chat_with_program(passwordprogram,name,chatsequence)); +} + +#else +BOOL chgpasswd(char *name,char *oldpass,char *newpass) +{ + DEBUG(0,("Password changing not compiled in (user=%s)\n",name)); + return(False); +} +#endif diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c new file mode 100644 index 0000000000..ac6f918b9d --- /dev/null +++ b/source3/smbd/dir.c @@ -0,0 +1,955 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Directory handling routines + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" + +extern int DEBUGLEVEL; +extern connection_struct Connections[]; + +/* + This module implements directory related functions for Samba. +*/ + + + +uint32 dircounter = 0; + + +#define NUMDIRPTRS 256 + + +static struct dptr_struct +{ + int pid; + int cnum; + uint32 lastused; + void *ptr; + BOOL valid; + BOOL finished; + BOOL expect_close; + char *wcard; /* Field only used for lanman2 trans2_findfirst/next searches */ + uint16 attr; /* Field only used for lanman2 trans2_findfirst/next searches */ + char *path; +} +dirptrs[NUMDIRPTRS]; + + +static int dptrs_open = 0; + +/**************************************************************************** +initialise the dir array +****************************************************************************/ +void init_dptrs(void) +{ + static BOOL dptrs_init=False; + int i; + + if (dptrs_init) return; + for (i=0;i<NUMDIRPTRS;i++) + { + dirptrs[i].valid = False; + dirptrs[i].wcard = NULL; + dirptrs[i].ptr = NULL; + string_init(&dirptrs[i].path,""); + } + dptrs_init = True; +} + +/**************************************************************************** +idle a dptr - the directory is closed but the control info is kept +****************************************************************************/ +static void dptr_idle(int key) +{ + if (dirptrs[key].valid && dirptrs[key].ptr) { + DEBUG(4,("Idling dptr key %d\n",key)); + dptrs_open--; + CloseDir(dirptrs[key].ptr); + dirptrs[key].ptr = NULL; + } +} + +/**************************************************************************** +idle the oldest dptr +****************************************************************************/ +static void dptr_idleoldest(void) +{ + int i; + uint32 old=dircounter+1; + int oldi= -1; + for (i=0;i<NUMDIRPTRS;i++) + if (dirptrs[i].valid && dirptrs[i].ptr && dirptrs[i].lastused < old) { + old = dirptrs[i].lastused; + oldi = i; + } + if (oldi != -1) + dptr_idle(oldi); + else + DEBUG(0,("No dptrs available to idle??\n")); +} + +/**************************************************************************** +get the dir ptr for a dir index +****************************************************************************/ +static void *dptr_get(int key,uint32 lastused) +{ + if (dirptrs[key].valid) { + if (lastused) dirptrs[key].lastused = lastused; + if (!dirptrs[key].ptr) { + if (dptrs_open >= MAXDIR) + dptr_idleoldest(); + DEBUG(4,("Reopening dptr key %d\n",key)); + if ((dirptrs[key].ptr = OpenDir(dirptrs[key].path))) + dptrs_open++; + } + return(dirptrs[key].ptr); + } + return(NULL); +} + +/**************************************************************************** +get the dir path for a dir index +****************************************************************************/ +char *dptr_path(int key) +{ + if (dirptrs[key].valid) + return(dirptrs[key].path); + return(NULL); +} + +/**************************************************************************** +get the dir wcard for a dir index (lanman2 specific) +****************************************************************************/ +char *dptr_wcard(int key) +{ + if (dirptrs[key].valid) + return(dirptrs[key].wcard); + return(NULL); +} + +/**************************************************************************** +set the dir wcard for a dir index (lanman2 specific) +Returns 0 on ok, 1 on fail. +****************************************************************************/ +BOOL dptr_set_wcard(int key, char *wcard) +{ + if (dirptrs[key].valid) { + dirptrs[key].wcard = wcard; + return True; + } + return False; +} + +/**************************************************************************** +set the dir attrib for a dir index (lanman2 specific) +Returns 0 on ok, 1 on fail. +****************************************************************************/ +BOOL dptr_set_attr(int key, uint16 attr) +{ + if (dirptrs[key].valid) { + dirptrs[key].attr = attr; + return True; + } + return False; +} + +/**************************************************************************** +get the dir attrib for a dir index (lanman2 specific) +****************************************************************************/ +uint16 dptr_attr(int key) +{ + if (dirptrs[key].valid) + return(dirptrs[key].attr); + return(0); +} + +/**************************************************************************** +close a dptr +****************************************************************************/ +void dptr_close(int key) +{ + if (dirptrs[key].valid) { + DEBUG(4,("closing dptr key %d\n",key)); + if (dirptrs[key].ptr) { + CloseDir(dirptrs[key].ptr); + dptrs_open--; + } + /* Lanman 2 specific code */ + if (dirptrs[key].wcard) + free(dirptrs[key].wcard); + dirptrs[key].valid = False; + string_set(&dirptrs[key].path,""); + } +} + +/**************************************************************************** +close all dptrs for a cnum +****************************************************************************/ +void dptr_closecnum(int cnum) +{ + int i; + for (i=0;i<NUMDIRPTRS;i++) + if (dirptrs[i].valid && dirptrs[i].cnum == cnum) + dptr_close(i); +} + +/**************************************************************************** +idle all dptrs for a cnum +****************************************************************************/ +void dptr_idlecnum(int cnum) +{ + int i; + for (i=0;i<NUMDIRPTRS;i++) + if (dirptrs[i].valid && dirptrs[i].cnum == cnum && dirptrs[i].ptr) + dptr_idle(i); +} + +/**************************************************************************** +close a dptr that matches a given path, only if it matches the pid also +****************************************************************************/ +void dptr_closepath(char *path,int pid) +{ + int i; + for (i=0;i<NUMDIRPTRS;i++) + if (dirptrs[i].valid && pid == dirptrs[i].pid && + strequal(dirptrs[i].path,path)) + dptr_close(i); +} + +/**************************************************************************** + start a directory listing +****************************************************************************/ +static BOOL start_dir(int cnum,char *directory) +{ + DEBUG(5,("start_dir cnum=%d dir=%s\n",cnum,directory)); + + if (!check_name(directory,cnum)) + return(False); + + if (! *directory) + directory = "."; + + Connections[cnum].dirptr = OpenDir(directory); + if (Connections[cnum].dirptr) { + dptrs_open++; + string_set(&Connections[cnum].dirpath,directory); + return(True); + } + + return(False); +} + + +/**************************************************************************** +create a new dir ptr +****************************************************************************/ +int dptr_create(int cnum,char *path, BOOL expect_close,int pid) +{ + int i; + uint32 old; + int oldi; + + if (!start_dir(cnum,path)) + return(-1); + + if (dptrs_open >= MAXDIR) + dptr_idleoldest(); + + for (i=0;i<NUMDIRPTRS;i++) + if (!dirptrs[i].valid) + break; + if (i == NUMDIRPTRS) i = -1; + + + /* as a 2nd option, grab the oldest not marked for expect_close */ + if (i == -1) { + old=dircounter+1; + oldi= -1; + for (i=0;i<NUMDIRPTRS;i++) + if (!dirptrs[i].expect_close && dirptrs[i].lastused < old) { + old = dirptrs[i].lastused; + oldi = i; + } + i = oldi; + } + + /* a 3rd option - grab the oldest one */ + if (i == -1) { + old=dircounter+1; + oldi= -1; + for (i=0;i<NUMDIRPTRS;i++) + if (dirptrs[i].lastused < old) { + old = dirptrs[i].lastused; + oldi = i; + } + i = oldi; + } + + if (i == -1) { + DEBUG(0,("Error - all dirptrs in use??\n")); + return(-1); + } + + if (dirptrs[i].valid) + dptr_close(i); + + dirptrs[i].ptr = Connections[cnum].dirptr; + string_set(&dirptrs[i].path,path); + dirptrs[i].lastused = dircounter++; + dirptrs[i].finished = False; + dirptrs[i].cnum = cnum; + dirptrs[i].pid = pid; + dirptrs[i].expect_close = expect_close; + dirptrs[i].wcard = NULL; /* Only used in lanman2 searches */ + dirptrs[i].attr = 0; /* Only used in lanman2 searches */ + dirptrs[i].valid = True; + + DEBUG(3,("creating new dirptr %d for path %s, expect_close = %d\n", + i,path,expect_close)); + + return(i); +} + +#define DPTR_MASK ((uint32)(((uint32)1)<<31)) + +/**************************************************************************** +fill the 5 byte server reserved dptr field +****************************************************************************/ +BOOL dptr_fill(char *buf1,unsigned int key) +{ + unsigned char *buf = (unsigned char *)buf1; + void *p = dptr_get(key,0); + uint32 offset; + if (!p) { + DEBUG(1,("filling null dirptr %d\n",key)); + return(False); + } + offset = TellDir(p); + DEBUG(6,("fill on key %d dirptr 0x%x now at %d\n",key,p,offset)); + buf[0] = key; + SIVAL(buf,1,offset | DPTR_MASK); + return(True); +} + + +/**************************************************************************** +return True is the offset is at zero +****************************************************************************/ +BOOL dptr_zero(char *buf) +{ + return((IVAL(buf,1)&~DPTR_MASK) == 0); +} + +/**************************************************************************** +fetch the dir ptr and seek it given the 5 byte server field +****************************************************************************/ +void *dptr_fetch(char *buf,int *num) +{ + unsigned int key = *(unsigned char *)buf; + void *p = dptr_get(key,dircounter++); + uint32 offset; + if (!p) { + DEBUG(3,("fetched null dirptr %d\n",key)); + return(NULL); + } + *num = key; + offset = IVAL(buf,1)&~DPTR_MASK; + SeekDir(p,offset); + DEBUG(3,("fetching dirptr %d for path %s at offset %d\n", + key,dptr_path(key),offset)); + return(p); +} + +/**************************************************************************** +fetch the dir ptr and seek it given the lanman2 parameter block +****************************************************************************/ +void *dptr_fetch_lanman2(char *params,int dptr_num) +{ + void *p = dptr_get(dptr_num,dircounter++); + uint32 resume_key = SVAL(params,6); + BOOL uses_resume_key = BITSETW(params+10,2); + BOOL continue_bit = BITSETW(params+10,3); + + if (!p) { + DEBUG(3,("fetched null dirptr %d\n",dptr_num)); + return(NULL); + } + if(uses_resume_key && !continue_bit) + SeekDir(p,resume_key); + DEBUG(3,("fetching dirptr %d for path %s\n",dptr_num,dptr_path(dptr_num))); + return(p); +} + +/**************************************************************************** + get a directory entry +****************************************************************************/ +BOOL get_dir_entry(int cnum,char *mask,int dirtype,char *fname,int *size,int *mode,time_t *date,BOOL check_descend) +{ + char *dname; + BOOL found = False; + struct stat sbuf; + pstring path; + pstring pathreal; + BOOL isrootdir; + pstring filename; + BOOL matched; + + *path = *pathreal = *filename = 0; + + isrootdir = (strequal(Connections[cnum].dirpath,"./") || + strequal(Connections[cnum].dirpath,".") || + strequal(Connections[cnum].dirpath,"/")); + + if (!Connections[cnum].dirptr) + return(False); + + while (!found) + { + dname = ReadDirName(Connections[cnum].dirptr); + + DEBUG(6,("readdir on dirptr 0x%x now at offset %d\n", + Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr))); + + if (dname == NULL) + return(False); + + matched = False; + + strcpy(filename,dname); + + if ((strcmp(filename,mask) == 0) || + (name_map_mangle(filename,True,SNUM(cnum)) && + mask_match(filename,mask,False,False))) + { + if (isrootdir && (strequal(filename,"..") || strequal(filename,"."))) + continue; + + strcpy(fname,filename); + *path = 0; + strcpy(path,Connections[cnum].dirpath); + strcat(path,"/"); + strcpy(pathreal,path); + strcat(path,fname); + strcat(pathreal,dname); + if (sys_stat(pathreal,&sbuf) != 0) + { + DEBUG(5,("Couldn't stat 1 [%s]\n",path)); + continue; + } + + if (check_descend && + !strequal(fname,".") && !strequal(fname,"..")) + continue; + + *mode = dos_mode(cnum,pathreal,&sbuf); + + if (((*mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) + { + DEBUG(5,("[%s] attribs didn't match %x\n",filename,dirtype)); + continue; + } + *size = sbuf.st_size; + *date = sbuf.st_mtime; + + DEBUG(5,("get_dir_entry found %s fname=%s\n",pathreal,fname)); + + found = True; + } + } + + return(found); +} + + + +typedef struct +{ + int pos; + int numentries; + int mallocsize; + char *data; + char *current; +} Dir; + + +/******************************************************************* +open a directory +********************************************************************/ +void *OpenDir(char *name) +{ + Dir *dirp; + char *n; + void *p = sys_opendir(name); + int used=0; + + if (!p) return(NULL); + dirp = (Dir *)malloc(sizeof(Dir)); + if (!dirp) { + closedir(p); + return(NULL); + } + dirp->pos = dirp->numentries = dirp->mallocsize = 0; + dirp->data = dirp->current = NULL; + + while ((n = readdirname(p))) { + int l = strlen(n)+1; + if (used + l > dirp->mallocsize) { + int s = MAX(used+l,used+2000); + char *r; + r = (char *)Realloc(dirp->data,s); + if (!r) { + DEBUG(0,("Out of memory in OpenDir\n")); + break; + } + dirp->data = r; + dirp->mallocsize = s; + dirp->current = dirp->data; + } + strcpy(dirp->data+used,n); + used += l; + dirp->numentries++; + } + + closedir(p); + return((void *)dirp); +} + + +/******************************************************************* +close a directory +********************************************************************/ +void CloseDir(void *p) +{ + Dir *dirp = (Dir *)p; + if (!dirp) return; + if (dirp->data) free(dirp->data); + free(dirp); +} + +/******************************************************************* +read from a directory +********************************************************************/ +char *ReadDirName(void *p) +{ + char *ret; + Dir *dirp = (Dir *)p; + + if (!dirp || !dirp->current || dirp->pos >= dirp->numentries) return(NULL); + + ret = dirp->current; + dirp->current = skip_string(dirp->current,1); + dirp->pos++; + + return(ret); +} + + +/******************************************************************* +seek a dir +********************************************************************/ +BOOL SeekDir(void *p,int pos) +{ + Dir *dirp = (Dir *)p; + + if (!dirp) return(False); + + if (pos < dirp->pos) { + dirp->current = dirp->data; + dirp->pos = 0; + } + + while (dirp->pos < pos && ReadDirName(p)) ; + + return(dirp->pos == pos); +} + +/******************************************************************* +tell a dir position +********************************************************************/ +int TellDir(void *p) +{ + Dir *dirp = (Dir *)p; + + if (!dirp) return(-1); + + return(dirp->pos); +} + + +static int dir_cache_size = 0; +static struct dir_cache { + struct dir_cache *next; + struct dir_cache *prev; + char *path; + char *name; + char *dname; + int snum; +} *dir_cache = NULL; + +/******************************************************************* +add an entry to the directory cache +********************************************************************/ +void DirCacheAdd(char *path,char *name,char *dname,int snum) +{ + struct dir_cache *entry = (struct dir_cache *)malloc(sizeof(*entry)); + if (!entry) return; + entry->path = strdup(path); + entry->name = strdup(name); + entry->dname = strdup(dname); + entry->snum = snum; + if (!entry->path || !entry->name || !entry->dname) return; + + entry->next = dir_cache; + entry->prev = NULL; + if (entry->next) entry->next->prev = entry; + dir_cache = entry; + + DEBUG(4,("Added dir cache entry %s %s -> %s\n",path,name,dname)); + + if (dir_cache_size == DIRCACHESIZE) { + for (entry=dir_cache; entry->next; entry=entry->next) ; + free(entry->path); + free(entry->name); + free(entry->dname); + if (entry->prev) entry->prev->next = entry->next; + free(entry); + } else { + dir_cache_size++; + } +} + + +/******************************************************************* +check for an entry in the directory cache +********************************************************************/ +char *DirCacheCheck(char *path,char *name,int snum) +{ + struct dir_cache *entry; + + for (entry=dir_cache; entry; entry=entry->next) { + if (entry->snum == snum && + strcmp(path,entry->path) == 0 && + strcmp(name,entry->name) == 0) { + DEBUG(4,("Got dir cache hit on %s %s -> %s\n",path,name,entry->dname)); + return(entry->dname); + } + } + + return(NULL); +} + +/******************************************************************* +flush entries in the dir_cache +********************************************************************/ +void DirCacheFlush(int snum) +{ + struct dir_cache *entry,*next; + + for (entry=dir_cache; entry; entry=next) { + if (entry->snum == snum) { + free(entry->path); + free(entry->dname); + free(entry->name); + next = entry->next; + if (entry->prev) entry->prev->next = entry->next; + if (entry->next) entry->next->prev = entry->prev; + if (dir_cache == entry) dir_cache = entry->next; + free(entry); + } else { + next = entry->next; + } + } +} + + +#ifdef REPLACE_GETWD +/* This is getcwd.c from bash. It is needed in Interactive UNIX. To + * add support for another OS you need to determine which of the + * conditional compilation macros you need to define. All the options + * are defined for Interactive UNIX. + */ +#ifdef ISC +#define HAVE_UNISTD_H +#define USGr3 +#define USG +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#if defined (__STDC__) +# define CONST const +# define PTR void * +#else /* !__STDC__ */ +# define CONST +# define PTR char * +#endif /* !__STDC__ */ + +#if !defined (PATH_MAX) +# if defined (MAXPATHLEN) +# define PATH_MAX MAXPATHLEN +# else /* !MAXPATHLEN */ +# define PATH_MAX 1024 +# endif /* !MAXPATHLEN */ +#endif /* !PATH_MAX */ + +#if defined (_POSIX_VERSION) || defined (USGr3) || defined (HAVE_DIRENT_H) +# if !defined (HAVE_DIRENT) +# define HAVE_DIRENT +# endif /* !HAVE_DIRENT */ +#endif /* _POSIX_VERSION || USGr3 || HAVE_DIRENT_H */ + +#if defined (HAVE_DIRENT) +# define D_NAMLEN(d) (strlen ((d)->d_name)) +#else +# define D_NAMLEN(d) ((d)->d_namlen) +#endif /* ! (_POSIX_VERSION || USGr3) */ + +#if defined (USG) || defined (USGr3) +# define d_fileno d_ino +#endif + +#if !defined (alloca) +extern char *alloca (); +#endif /* alloca */ + +/* Get the pathname of the current working directory, + and put it in SIZE bytes of BUF. Returns NULL if the + directory couldn't be determined or SIZE was too small. + If successful, returns BUF. In GNU, if BUF is NULL, + an array is allocated with `malloc'; the array is SIZE + bytes long, unless SIZE <= 0, in which case it is as + big as necessary. */ +#if defined (__STDC__) +char * +getcwd (char *buf, size_t size) +#else /* !__STDC__ */ +char * +getcwd (buf, size) + char *buf; + int size; +#endif /* !__STDC__ */ +{ + static CONST char dots[] + = "../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../.."; + CONST char *dotp, *dotlist; + size_t dotsize; + dev_t rootdev, thisdev; + ino_t rootino, thisino; + char path[PATH_MAX + 1]; + register char *pathp; + char *pathbuf; + size_t pathsize; + struct stat st; + + if (buf != NULL && size == 0) + { + errno = EINVAL; + return ((char *)NULL); + } + + pathsize = sizeof (path); + pathp = &path[pathsize]; + *--pathp = '\0'; + pathbuf = path; + + if (stat (".", &st) < 0) + return ((char *)NULL); + thisdev = st.st_dev; + thisino = st.st_ino; + + if (stat ("/", &st) < 0) + return ((char *)NULL); + rootdev = st.st_dev; + rootino = st.st_ino; + + dotsize = sizeof (dots) - 1; + dotp = &dots[sizeof (dots)]; + dotlist = dots; + while (!(thisdev == rootdev && thisino == rootino)) + { + register DIR *dirstream; + register struct dirent *d; + dev_t dotdev; + ino_t dotino; + char mount_point; + int namlen; + + /* Look at the parent directory. */ + if (dotp == dotlist) + { + /* My, what a deep directory tree you have, Grandma. */ + char *new; + if (dotlist == dots) + { + new = malloc (dotsize * 2 + 1); + if (new == NULL) + goto lose; + memcpy (new, dots, dotsize); + } + else + { + new = realloc ((PTR) dotlist, dotsize * 2 + 1); + if (new == NULL) + goto lose; + } + memcpy (&new[dotsize], new, dotsize); + dotp = &new[dotsize]; + dotsize *= 2; + new[dotsize] = '\0'; + dotlist = new; + } + + dotp -= 3; + + /* Figure out if this directory is a mount point. */ + if (stat (dotp, &st) < 0) + goto lose; + dotdev = st.st_dev; + dotino = st.st_ino; + mount_point = dotdev != thisdev; + + /* Search for the last directory. */ + dirstream = opendir(dotp); + if (dirstream == NULL) + goto lose; + while ((d = (struct dirent *)readdir(dirstream)) != NULL) + { + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + if (mount_point || d->d_fileno == thisino) + { + char *name; + + namlen = D_NAMLEN(d); + name = (char *) + alloca (dotlist + dotsize - dotp + 1 + namlen + 1); + memcpy (name, dotp, dotlist + dotsize - dotp); + name[dotlist + dotsize - dotp] = '/'; + memcpy (&name[dotlist + dotsize - dotp + 1], + d->d_name, namlen + 1); + if (lstat (name, &st) < 0) + { + int save = errno; + closedir(dirstream); + errno = save; + goto lose; + } + if (st.st_dev == thisdev && st.st_ino == thisino) + break; + } + } + if (d == NULL) + { + int save = errno; + closedir(dirstream); + errno = save; + goto lose; + } + else + { + size_t space; + + while ((space = pathp - pathbuf) <= namlen) + { + char *new; + + if (pathbuf == path) + { + new = malloc (pathsize * 2); + if (!new) + goto lose; + } + else + { + new = realloc ((PTR) pathbuf, (pathsize * 2)); + if (!new) + goto lose; + pathp = new + space; + } + (void) memcpy (new + pathsize + space, pathp, pathsize - space); + pathp = new + pathsize + space; + pathbuf = new; + pathsize *= 2; + } + + pathp -= namlen; + (void) memcpy (pathp, d->d_name, namlen); + *--pathp = '/'; + closedir(dirstream); + } + + thisdev = dotdev; + thisino = dotino; + } + + if (pathp == &path[sizeof(path) - 1]) + *--pathp = '/'; + + if (dotlist != dots) + free ((PTR) dotlist); + + { + size_t len = pathbuf + pathsize - pathp; + if (buf == NULL) + { + if (len < (size_t) size) + len = size; + buf = (char *) malloc (len); + if (buf == NULL) + goto lose2; + } + else if ((size_t) size < len) + { + errno = ERANGE; + goto lose2; + } + (void) memcpy((PTR) buf, (PTR) pathp, len); + } + + if (pathbuf != path) + free (pathbuf); + + return (buf); + + lose: + if ((dotlist != dots) && dotlist) + { + int e = errno; + free ((PTR) dotlist); + errno = e; + } + + lose2: + if ((pathbuf != path) && pathbuf) + { + int e = errno; + free ((PTR) pathbuf); + errno = e; + } + return ((char *)NULL); +} +#endif diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c new file mode 100644 index 0000000000..8852e57e8b --- /dev/null +++ b/source3/smbd/ipc.c @@ -0,0 +1,2779 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Inter-process communication and named pipe handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. + */ +/* + This file handles the named pipe and mailslot calls + in the SMBtrans protocol + */ + +#include "includes.h" +#include "loadparm.h" +#include "pcap.h" + +#ifdef CHECK_TYPES +#undef CHECK_TYPES +#endif +#define CHECK_TYPES 0 + +extern int DEBUGLEVEL; +extern int maxxmit; +extern files_struct Files[]; +extern connection_struct Connections[]; + +extern fstring local_machine; + +#define NERR_Success 0 +#define NERR_badpass 86 +#define NERR_notsupported 50 + +#define NERR_BASE (2100) +#define NERR_BufTooSmall (NERR_BASE+23) +#define NERR_JobNotFound (NERR_BASE+51) +#define NERR_DestNotFound (NERR_BASE+52) +#define ERROR_INVALID_LEVEL 124 +#define ERROR_MORE_DATA 234 + +#define REALLOC(ptr,size) Realloc(ptr,MAX((size),4*1024)) + +#define ACCESS_READ 0x01 +#define ACCESS_WRITE 0x02 +#define ACCESS_CREATE 0x04 + +#define SHPWLEN 8 /* share password length */ +#define NNLEN 12 /* 8.3 net name length */ +#define SNLEN 15 /* service name length */ +#define QNLEN 12 /* queue name maximum length */ + +extern int Client; + +static int CopyExpanded(int cnum, int snum, char** dst, char* src, int* n) +{ + pstring buf; + int l; + + if (!src || !dst || !n || !(*dst)) return(0); + + StrnCpy(buf,src,sizeof(buf)/2); + string_sub(buf,"%S",lp_servicename(snum)); + standard_sub(cnum,buf); + StrnCpy(*dst,buf,*n); + l = strlen(*dst) + 1; + (*dst) += l; + (*n) -= l; + return l; +} + +static int CopyAndAdvance(char** dst, char* src, int* n) +{ + int l; + if (!src || !dst || !n || !(*dst)) return(0); + StrnCpy(*dst,src,*n); + l = strlen(*dst) + 1; + (*dst) += l; + (*n) -= l; + return l; +} + +static int StrlenExpanded(int cnum, int snum, char* s) +{ + pstring buf; + if (!s) return(0); + StrnCpy(buf,s,sizeof(buf)/2); + string_sub(buf,"%S",lp_servicename(snum)); + standard_sub(cnum,buf); + return strlen(buf) + 1; +} + +static char* Expand(int cnum, int snum, char* s) +{ + static pstring buf; + if (!s) return(NULL); + StrnCpy(buf,s,sizeof(buf)/2); + string_sub(buf,"%S",lp_servicename(snum)); + standard_sub(cnum,buf); + return &buf[0]; +} + +/******************************************************************* + check a API string for validity when we only need to check the prefix + ******************************************************************/ +static BOOL prefix_ok(char *str,char *prefix) +{ + return(strncmp(str,prefix,strlen(prefix)) == 0); +} + + +/**************************************************************************** + send a trans reply + ****************************************************************************/ +static void send_trans_reply(char *outbuf,char *data,char *param,uint16 *setup, + int ldata,int lparam,int lsetup) +{ + int i; + int this_ldata,this_lparam; + int tot_data=0,tot_param=0; + int align; + + this_lparam = MIN(lparam,maxxmit - (500+lsetup*SIZEOFWORD)); /* hack */ + this_ldata = MIN(ldata,maxxmit - (500+lsetup*SIZEOFWORD+this_lparam)); + + align = (this_lparam%4); + + set_message(outbuf,10+lsetup,align+this_ldata+this_lparam,True); + if (this_lparam) + memcpy(smb_buf(outbuf),param,this_lparam); + if (this_ldata) + memcpy(smb_buf(outbuf)+this_lparam+align,data,this_ldata); + + SSVAL(outbuf,smb_vwv0,lparam); + SSVAL(outbuf,smb_vwv1,ldata); + SSVAL(outbuf,smb_vwv3,this_lparam); + SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf)); + SSVAL(outbuf,smb_vwv5,0); + SSVAL(outbuf,smb_vwv6,this_ldata); + SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf)); + SSVAL(outbuf,smb_vwv8,0); + SSVAL(outbuf,smb_vwv9,lsetup); + for (i=0;i<lsetup;i++) + SSVAL(outbuf,smb_vwv10+i*SIZEOFWORD,setup[i]); + + show_msg(outbuf); + send_smb(Client,outbuf); + + tot_data = this_ldata; + tot_param = this_lparam; + + while (tot_data < ldata || tot_param < lparam) + { + this_lparam = MIN(lparam-tot_param,maxxmit - 500); /* hack */ + this_ldata = MIN(ldata-tot_data,maxxmit - (500+this_lparam)); + + align = (this_lparam%4); + + set_message(outbuf,10,this_ldata+this_lparam+align,False); + if (this_lparam) + memcpy(smb_buf(outbuf),param+tot_param,this_lparam); + if (this_ldata) + memcpy(smb_buf(outbuf)+this_lparam+align,data+tot_data,this_ldata); + + SSVAL(outbuf,smb_vwv3,this_lparam); + SSVAL(outbuf,smb_vwv4,smb_offset(smb_buf(outbuf),outbuf)); + SSVAL(outbuf,smb_vwv5,tot_param); + SSVAL(outbuf,smb_vwv6,this_ldata); + SSVAL(outbuf,smb_vwv7,smb_offset(smb_buf(outbuf)+this_lparam+align,outbuf)); + SSVAL(outbuf,smb_vwv8,tot_data); + SSVAL(outbuf,smb_vwv9,0); + + show_msg(outbuf); + send_smb(Client,outbuf); + + tot_data += this_ldata; + tot_param += this_lparam; + } +} + + + +/**************************************************************************** + get a print queue + ****************************************************************************/ + +struct pack_desc { + char* format; /* formatstring for structure */ + char* subformat; /* subformat for structure */ + char* base; /* baseaddress of buffer */ + int buflen; /* remaining size for fixed part; on init: length of base */ + int subcount; /* count of substructures */ + char* structbuf; /* pointer into buffer for remaining fixed part */ + int stringlen; /* remaining size for variable part */ + char* stringbuf; /* pointer into buffer for remaining variable part */ + int neededlen; /* total needed size */ + int usedlen; /* total used size (usedlen <= neededlen and usedlen <= buflen) */ + char* curpos; /* current position; pointer into format or subformat */ + int errcode; +}; + +static int get_counter(char** p) +{ + int i, n; + if (!p || !(*p)) return(1); + if (!isdigit(**p)) return 1; + for (n = 0;;) { + i = **p; + if (isdigit(i)) + n = 10 * n + (i - '0'); + else + return n; + (*p)++; + } +} + +static int getlen(char* p) +{ + int n = 0; + if (!p) return(0); + while (*p) { + switch( *p++ ) { + case 'W': /* word (2 byte) */ + n += 2; + break; + case 'N': /* count of substructures (word) at end */ + n += 2; + break; + case 'D': /* double word (4 byte) */ + case 'z': /* offset to zero terminated string (4 byte) */ + case 'l': /* offset to user data (4 byte) */ + n += 4; + break; + case 'b': /* offset to data (with counter) (4 byte) */ + n += 4; + get_counter(&p); + break; + case 'B': /* byte (with optional counter) */ + n += get_counter(&p); + break; + } + } + return n; +} + +static BOOL init_package(struct pack_desc* p, int count, int subcount) +{ + int n = p->buflen; + int i; + + if (!p->format || !p->base) return(False); + + i = count * getlen(p->format); + if (p->subformat) i += subcount * getlen(p->subformat); + p->structbuf = p->base; + p->neededlen = 0; + p->usedlen = 0; + p->subcount = 0; + p->curpos = p->format; + if (i > n) { + i = n = 0; + p->errcode = NERR_BufTooSmall; + } + + p->errcode = NERR_Success; + p->buflen = i; + n -= i; + p->stringbuf = p->base + i; + p->stringlen = n; + return(p->errcode == NERR_Success); +} + +#ifdef __STDC__ +static int package(struct pack_desc* p, ...) +{ +#else +static int package(va_alist) +va_dcl +{ + struct pack_desc* p; +#endif + va_list args; + int needed=0, stringneeded; + char* str=NULL; + int is_string=0, stringused; + int32 temp; + +#ifdef __STDC__ + va_start(args,p); +#else + va_start(args); + p = va_arg(args,struct pack_desc *); +#endif + + if (!*p->curpos) { + if (!p->subcount) + p->curpos = p->format; + else { + p->curpos = p->subformat; + p->subcount--; + } + } +#if CHECK_TYPES + str = va_arg(args,char*); + if (strncmp(str,p->curpos,strlen(str)) != 0) { + DEBUG(2,("type error in package: %s instead of %*s\n",str, + strlen(str),p->curpos)); + va_end(args); +#if AJT + ajt_panic(); +#endif + return 0; + } +#endif + stringneeded = -1; + + if (!p->curpos) return(0); + + switch( *p->curpos++ ) { + case 'W': /* word (2 byte) */ + needed = 2; + temp = va_arg(args,int); + if (p->buflen >= needed) SSVAL(p->structbuf,0,temp); + break; + case 'N': /* count of substructures (word) at end */ + needed = 2; + p->subcount = va_arg(args,int); + if (p->buflen >= needed) SSVAL(p->structbuf,0,p->subcount); + break; + case 'D': /* double word (4 byte) */ + needed = 4; + temp = va_arg(args,int); + if (p->buflen >= needed) SIVAL(p->structbuf,0,temp); + break; + case 'B': /* byte (with optional counter) */ + needed = get_counter(&p->curpos); + { + char *s = va_arg(args,char*); + if (p->buflen >= needed) StrnCpy(p->structbuf,s?s:"",needed); + } + break; + case 'z': /* offset to zero terminated string (4 byte) */ + str = va_arg(args,char*); + stringneeded = (str ? strlen(str)+1 : 0); + is_string = 1; + break; + case 'l': /* offset to user data (4 byte) */ + str = va_arg(args,char*); + stringneeded = va_arg(args,int); + is_string = 0; + break; + case 'b': /* offset to data (with counter) (4 byte) */ + str = va_arg(args,char*); + stringneeded = get_counter(&p->curpos); + is_string = 0; + break; + } + va_end(args); + if (stringneeded >= 0) { + needed = 4; + if (p->buflen >= needed) { + stringused = stringneeded; + if (stringused > p->stringlen) { + stringused = (is_string ? p->stringlen : 0); + if (p->errcode == NERR_Success) p->errcode = ERROR_MORE_DATA; + } + if (!stringused) + SIVAL(p->structbuf,0,0); + else { + SIVAL(p->structbuf,0,PTR_DIFF(p->stringbuf,p->base)); + memcpy(p->stringbuf,str?str:"",stringused); + if (is_string) p->stringbuf[stringused-1] = '\0'; + p->stringbuf += stringused; + p->stringlen -= stringused; + p->usedlen += stringused; + } + } + p->neededlen += stringneeded; + } + p->neededlen += needed; + if (p->buflen >= needed) { + p->structbuf += needed; + p->buflen -= needed; + p->usedlen += needed; + } + else { + if (p->errcode == NERR_Success) p->errcode = NERR_BufTooSmall; + } + return 1; +} + +#if CHECK_TYPES +#define PACK(desc,t,v) package(desc,t,v,0,0,0,0) +#define PACKl(desc,t,v,l) package(desc,t,v,l,0,0,0,0) +#else +#define PACK(desc,t,v) package(desc,v) +#define PACKl(desc,t,v,l) package(desc,v,l) +#endif + +static void PACKI(struct pack_desc* desc,char *t,int v) +{ + PACK(desc,t,v); +} + +static void PACKS(struct pack_desc* desc,char *t,char *v) +{ + PACK(desc,t,v); +} + +static void PackDriverData(struct pack_desc* desc) +{ + char drivdata[4+4+32]; + SIVAL(drivdata,0,sizeof drivdata); /* cb */ + SIVAL(drivdata,4,1000); /* lVersion */ + memset(drivdata+8,0,32); /* szDeviceName */ + strcpy(drivdata+8,"NULL"); + PACKl(desc,"l",drivdata,sizeof drivdata); /* pDriverData */ +} + +static int check_printq_info(struct pack_desc* desc, + int uLevel, const char* id1, const char* id2) +{ + desc->subformat = NULL; + switch( uLevel ) { + case 0: + desc->format = "B13"; + break; + case 1: + desc->format = "B13BWWWzzzzzWW"; + break; + case 2: + desc->format = "B13BWWWzzzzzWN"; + desc->subformat = "WB21BB16B10zWWzDDz"; + break; + case 3: + desc->format = "zWWWWzzzzWWzzl"; + break; + case 4: + desc->format = "zWWWWzzzzWNzzl"; + desc->subformat = "WWzWWDDzz"; + break; + case 5: + desc->format = "z"; + break; + default: return False; + } + if (strcmp(desc->format,id1) != 0) return False; + if (desc->subformat && strcmp(desc->subformat,id2) != 0) return False; + return True; +} + +static void fill_printjob_info(int cnum, int snum, int uLevel, + struct pack_desc* desc, + print_queue_struct* queue, int n) +{ + time_t t = queue->time; + + /* the client expects localtime */ + t += GMT_TO_LOCAL*TimeDiff(t); + + PACKI(desc,"W",((snum%0xFF)<<8) | (queue->job%0xFF)); /* uJobId */ + if (uLevel == 1) { + PACKS(desc,"B21",queue->user); /* szUserName */ + PACKS(desc,"B",""); /* pad */ + PACKS(desc,"B16",""); /* szNotifyName */ + PACKS(desc,"B10","PM_Q_RAW"); /* szDataType */ + PACKS(desc,"z",""); /* pszParms */ + PACKI(desc,"W",n+1); /* uPosition */ + PACKI(desc,"W",queue->status); /* fsStatus */ + PACKS(desc,"z",""); /* pszStatus */ + PACKI(desc,"D",queue->time); /* ulSubmitted */ + PACKI(desc,"D",queue->size); /* ulSize */ + PACKS(desc,"z",queue->file); /* pszComment */ + } + if (uLevel == 2 || uLevel == 3) { + PACKI(desc,"W",queue->priority); /* uPriority */ + PACKS(desc,"z",queue->user); /* pszUserName */ + PACKI(desc,"W",n+1); /* uPosition */ + PACKI(desc,"W",queue->status); /* fsStatus */ + PACKI(desc,"D",queue->time); /* ulSubmitted */ + PACKI(desc,"D",queue->size); /* ulSize */ + PACKS(desc,"z","Samba"); /* pszComment */ + PACKS(desc,"z",queue->file); /* pszDocument */ + if (uLevel == 3) { + PACKS(desc,"z",""); /* pszNotifyName */ + PACKS(desc,"z","PM_Q_RAW"); /* pszDataType */ + PACKS(desc,"z",""); /* pszParms */ + PACKS(desc,"z",""); /* pszStatus */ + PACKS(desc,"z",SERVICE(snum)); /* pszQueue */ + PACKS(desc,"z","lpd"); /* pszQProcName */ + PACKS(desc,"z",""); /* pszQProcParms */ + PACKS(desc,"z","NULL"); /* pszDriverName */ + PackDriverData(desc); /* pDriverData */ + PACKS(desc,"z",""); /* pszPrinterName */ + } + } +} + +static void fill_printq_info(int cnum, int snum, int uLevel, + struct pack_desc* desc, + int count, print_queue_struct* queue, + print_status_struct* status) +{ + if (uLevel < 3) { + PACKS(desc,"B13",SERVICE(snum)); + } else { + PACKS(desc,"z",Expand(cnum,snum,SERVICE(snum))); + } + if (uLevel == 1 || uLevel == 2) { + PACKS(desc,"B",""); /* alignment */ + PACKI(desc,"W",5); /* priority */ + PACKI(desc,"W",0); /* start time */ + PACKI(desc,"W",0); /* until time */ + PACKS(desc,"z",""); /* pSepFile */ + PACKS(desc,"z","lpd"); /* pPrProc */ + PACKS(desc,"z",SERVICE(snum)); /* pDestinations */ + PACKS(desc,"z",""); /* pParms */ + if (snum < 0) { + PACKS(desc,"z","UNKNOWN PRINTER"); + PACKI(desc,"W",LPSTAT_ERROR); + } + else if (!status || !status->message[0]) { + PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); + PACKI(desc,"W",LPSTAT_OK); /* status */ + } else { + PACKS(desc,"z",status->message); + PACKI(desc,"W",status->status); /* status */ + } + PACKI(desc,(uLevel == 1 ? "W" : "N"),count); + } + if (uLevel == 3 || uLevel == 4) { + PACKI(desc,"W",5); /* uPriority */ + PACKI(desc,"W",0); /* uStarttime */ + PACKI(desc,"W",0); /* uUntiltime */ + PACKI(desc,"W",5); /* pad1 */ + PACKS(desc,"z",""); /* pszSepFile */ + PACKS(desc,"z","lpd"); /* pszPrProc */ + PACKS(desc,"z",""); /* pszParms */ + if (!status || !status->message[0]) { + PACKS(desc,"z",Expand(cnum,snum,lp_comment(snum))); /* pszComment */ + PACKI(desc,"W",LPSTAT_OK); /* fsStatus */ + } else { + PACKS(desc,"z",status->message); /* pszComment */ + PACKI(desc,"W",status->status); /* fsStatus */ + } + PACKI(desc,(uLevel == 3 ? "W" : "N"),count); /* cJobs */ + PACKS(desc,"z",SERVICE(snum)); /* pszPrinters */ + PACKS(desc,"z","NULL"); /* pszDriverName */ + PackDriverData(desc); /* pDriverData */ + } + if (uLevel == 2 || uLevel == 4) { + int i; + for (i=0;i<count;i++) + fill_printjob_info(cnum,snum,uLevel == 2 ? 1 : 2,desc,&queue[i],i); + } + + DEBUG(3,("fill_printq_info on <%s> gave %d entries\n",SERVICE(snum),count)); +} + +static BOOL api_DosPrintQGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + char *QueueName = p; + int uLevel,cbBuf; + int count=0; + int snum; + char* str3; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + + bzero(&status,sizeof(status)); + bzero(&desc,sizeof(desc)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + str3 = p + 4; + + if ((p = strchr(QueueName,'%'))) *p = 0; + + DEBUG(3,("PrintQueue uLevel=%d name=%s\n",uLevel,QueueName)); + + /* check it's a supported varient */ + if (!prefix_ok(str1,"zWrLh")) return False; + if (!check_printq_info(&desc,uLevel,str2,str3)) return False; + + snum = lp_servicenumber(QueueName); + if (snum < 0 && pcap_printername_ok(QueueName,NULL)) { + int pnum = lp_servicenumber(PRINTERS_NAME); + if (pnum >= 0) { + lp_add_printer(QueueName,pnum); + snum = lp_servicenumber(QueueName); + } + } + + if (snum < 0 || !VALID_SNUM(snum)) return(False); + + count = get_printqueue(snum,cnum,&queue,&status); + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + if (init_package(&desc,1,count)) { + desc.subcount = count; + fill_printq_info(cnum,snum,uLevel,&desc,count,queue,&status); + } + + *rdata_len = desc.usedlen; + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,desc.neededlen); + + DEBUG(4,("printqgetinfo: errorcode %d\n",desc.errcode)); + + if (queue) free(queue); + + return(True); +} + + +/**************************************************************************** + view list of all print jobs on all queues + ****************************************************************************/ +static BOOL api_DosPrintQEnum(int cnum, int uid, char* param, char* data, + int mdrcnt, int mprcnt, + char **rdata, char** rparam, + int *rdata_len, int *rparam_len) +{ + char *param_format = param+2; + char *output_format1 = skip_string(param_format,1); + char *p = skip_string(output_format1,1); + int uLevel = SVAL(p,0); + char *output_format2 = p + 4; + int services = lp_numservices(); + int i, n; + struct pack_desc desc; + print_queue_struct **queue = NULL; + print_status_struct *status = NULL; + int* subcntarr = NULL; + int queuecnt, subcnt=0, succnt=0; + + bzero(&desc,sizeof(desc)); + + DEBUG(3,("DosPrintQEnum uLevel=%d\n",uLevel)); + + if (prefix_ok(param_format,"WrLeh")) return False; + if (!check_printq_info(&desc,uLevel,output_format1,output_format2)) + return False; + queuecnt = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) + queuecnt++; + if (uLevel > 0) { + queue = (print_queue_struct**)malloc(queuecnt*sizeof(print_queue_struct*)); + memset(queue,0,queuecnt*sizeof(print_queue_struct*)); + status = (print_status_struct*)malloc(queuecnt*sizeof(print_status_struct)); + memset(status,0,queuecnt*sizeof(print_status_struct)); + subcntarr = (int*)malloc(queuecnt*sizeof(int)); + subcnt = 0; + n = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { + subcntarr[n] = get_printqueue(i,cnum,&queue[n],&status[n]); + subcnt += subcntarr[n]; + n++; + } + } + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + + if (init_package(&desc,queuecnt,subcnt)) { + n = 0; + succnt = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { + fill_printq_info(cnum,i,uLevel,&desc,subcntarr[n],queue[n],&status[n]); + n++; + if (desc.errcode == NERR_Success) succnt = n; + } + } + + if (subcntarr) free(subcntarr); + + *rdata_len = desc.usedlen; + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,queuecnt); + + for (i = 0; i < queuecnt; i++) { + if (queue && queue[i]) free(queue[i]); + } + + if (queue) free(queue); + if (status) free(status); + + return True; +} + +/**************************************************************************** + get info level for a server list query + ****************************************************************************/ +static BOOL check_server_info(int uLevel, char* id) +{ + switch( uLevel ) { + case 0: + if (strcmp(id,"B16") != 0) return False; + break; + case 1: + if (strcmp(id,"B16BBDz") != 0) return False; + break; + default: + return False; + } + return True; +} + +/* used for server information: client, nameserv and ipc */ +struct srv_info_struct +{ + fstring name; + uint32 type; + fstring comment; + fstring domain; /* used ONLY in ipc.c NOT namework.c */ + BOOL server_added; /* used ONLY in ipc.c NOT namework.c */ +}; + +/******************************************************************* + filter out unwanted server info + ******************************************************************/ +static BOOL filter_server_info(struct srv_info_struct *server, + char *domain) +{ + if (*domain) + return(strequal(domain, server->domain)); + + return (True); /* be indiscriminate: get all servers! */ +} + +/******************************************************************* + find server in the files saved by nmbd. Return True if we find it. + ******************************************************************/ +static BOOL find_server(struct srv_info_struct *servers, int num_servers, + char *domain, char *name) +{ + int count; + + if (!servers || num_servers == 0) return (False); + + for (count = 0; count < num_servers; count++) { + struct srv_info_struct *s; + + s = &servers[count]; + + if (strequal(name, s->name)) { + StrnCpy(domain, s->domain, sizeof(pstring)-1); + return (True); + } + } + return (False); +} + + +/******************************************************************* + get server info lists from the files saved by nmbd. Return the + number of entries + ******************************************************************/ +static int get_server_info(uint32 servertype, + struct srv_info_struct **servers) +{ + FILE *f; + pstring fname; + int count=0; + int alloced=0; + pstring line; + + strcpy(fname,lp_lockdir()); + trim_string(fname,NULL,"/"); + strcat(fname,"/"); + strcat(fname,SERVER_LIST); + + f = fopen(fname,"r"); + + if (!f) { + DEBUG(4,("Can't open %s - %s\n",fname,strerror(errno))); + return(0); + } + if (servertype == SV_TYPE_ALL) servertype &= ~SV_TYPE_DOMAIN_ENUM; + + while (!feof(f)) + { + fstring stype; + struct srv_info_struct *s; + char *ptr = line; + *ptr = 0; + + fgets(line,sizeof(line)-1,f); + if (!*line) continue; + + if (count == alloced) { + alloced += 10; + (*servers) = (struct srv_info_struct *) + Realloc(*servers,sizeof(**servers)*alloced); + if (!(*servers)) return(0); + bzero((char *)((*servers)+count),sizeof(**servers)*(alloced-count)); + } + s = &(*servers)[count]; + + s->server_added = True; + + if (!next_token(&ptr,s->name , NULL)) continue; + if (!next_token(&ptr,stype , NULL)) continue; + if (!next_token(&ptr,s->comment, NULL)) continue; + if (!next_token(&ptr,s->domain , NULL)) { + /* this allows us to cop with an old nmbd */ + strcpy(s->domain,my_workgroup()); + } + + if (sscanf(stype,"%X",&s->type) != 1) continue; + + /* doesn't match up: don't want it */ + if (!(servertype & s->type)) continue; + + /* server entry is a domain, we haven't asked for domains: don't want it */ + if ((s->type&SV_TYPE_DOMAIN_ENUM) && !(servertype&SV_TYPE_DOMAIN_ENUM)) + continue; + + DEBUG(4,("Server %20s %8x %25s %15s\n", + s->name, stype, s->comment, s->domain)); + + count++; + } + + fclose(f); + return(count); +} + +/******************************************************************* + fill in a server info structure + ******************************************************************/ +static int fill_srv_info(struct srv_info_struct *service, + int uLevel, char **buf, int *buflen, + char **stringbuf, int *stringspace, char *baseaddr) +{ + int struct_len; + char* p; + char* p2; + int l2; + int len; + + switch (uLevel) { + case 0: struct_len = 16; break; + case 1: struct_len = 26; break; + default: return -1; + } + + if (!buf) + { + len = 0; + switch (uLevel) + { + case 1: + len = strlen(service->comment)+1; + break; + } + + if (buflen) *buflen = struct_len; + if (stringspace) *stringspace = len; + return struct_len + len; + } + + len = struct_len; + p = *buf; + if (*buflen < struct_len) return -1; + if (stringbuf) + { + p2 = *stringbuf; + l2 = *stringspace; + } + else + { + p2 = p + struct_len; + l2 = *buflen - struct_len; + } + if (!baseaddr) baseaddr = p; + + switch (uLevel) + { + case 0: + StrnCpy(p,service->name,15); + break; + + case 1: + StrnCpy(p,service->name,15); + SIVAL(p,18,service->type); + SIVAL(p,22,PTR_DIFF(p2,baseaddr)); + len += CopyAndAdvance(&p2,service->comment,&l2); + break; + } + + if (stringbuf) + { + *buf = p + struct_len; + *buflen -= struct_len; + *stringbuf = p2; + *stringspace = l2; + } + else + { + *buf = p2; + *buflen -= len; + } + return len; +} + + +/**************************************************************************** + view list of servers available (or possibly domains). The info is + extracted from lists saved by nmbd on the local host + ****************************************************************************/ +static BOOL api_RNetServerEnum(int cnum, int uid, char *param, char *data, + int mdrcnt, int mprcnt, char **rdata, + char **rparam, int *rdata_len, int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + int buf_len = SVAL(p,2); + uint32 servertype = IVAL(p,4); + char *p2; + int data_len, fixed_len, string_len; + int f_len, s_len; + struct srv_info_struct *servers=NULL; + int counted=0,total=0; + int i; + fstring domain; + BOOL domain_request = (servertype & SV_TYPE_DOMAIN_ENUM) && + !(servertype == SV_TYPE_ALL); + + domain[0] = 0; + p += 8; + + if (!prefix_ok(str1,"WrLehD")) return False; + if (!check_server_info(uLevel,str2)) return False; + + DEBUG(4, ("server request level: %s\n", str2)); + + if (strcmp(str1, "WrLehDO") == 0) + { + /* asking for servers. we will have to work out which workgroup was + requested, as we maintain lists for multiple workgroups */ + } + else if (strcmp(str1, "WrLehDz") == 0) + { + /* asking for a specific workgroup */ + StrnCpy(domain, p, sizeof(fstring)-1); + } + + if (lp_browse_list()) + { + total = get_server_info(servertype,&servers); + } + + if (!domain[0] && !domain_request) { + extern fstring remote_machine; + /* must be a server request with an assumed domain. find a domain */ + + if (find_server(servers, total, domain, remote_machine)) { + DEBUG(4, ("No domain specified: using %s for %s\n", + domain, remote_machine)); + } else { + /* default to soemthing sensible */ + strcpy(domain,my_workgroup()); + } + } + + data_len = fixed_len = string_len = 0; + + for (i=0;i<total;i++) + if (filter_server_info(&servers[i],domain)) { + data_len += fill_srv_info(&servers[i],uLevel,0,&f_len,0,&s_len,0); + if (data_len <= buf_len) + { + counted++; + fixed_len += f_len; + string_len += s_len; + } + } + + *rdata_len = fixed_len + string_len; + *rdata = REALLOC(*rdata,*rdata_len); + bzero(*rdata,*rdata_len); + + p2 = (*rdata) + fixed_len; /* auxilliary data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + + { + int count2 = counted; + for (i = 0; i < total && count2;i++) { + if (filter_server_info(&servers[i],domain)) { + fill_srv_info(&servers[i],uLevel,&p,&f_len,&p2,&s_len,*rdata); + count2--; + } + } + } + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,total); + + if (servers) free(servers); + + DEBUG(3,("NetServerEnum domain = %s uLevel=%d counted=%d total=%d\n", + domain,uLevel,counted,total)); + + return(True); +} + + +/**************************************************************************** + get info about a share + ****************************************************************************/ +static BOOL check_share_info(int uLevel, char* id) +{ + switch( uLevel ) { + case 0: + if (strcmp(id,"B13") != 0) return False; + break; + case 1: + if (strcmp(id,"B13BWz") != 0) return False; + break; + case 2: + if (strcmp(id,"B13BWzWWWzB9B") != 0) return False; + break; + case 91: + if (strcmp(id,"B13BWzWWWzB9BB9BWzWWzWW") != 0) return False; + break; + default: return False; + } + return True; +} + +static int fill_share_info(int cnum, int snum, int uLevel, + char** buf, int* buflen, + char** stringbuf, int* stringspace, char* baseaddr) +{ + int struct_len; + char* p; + char* p2; + int l2; + int len; + + switch( uLevel ) { + case 0: struct_len = 13; break; + case 1: struct_len = 20; break; + case 2: struct_len = 40; break; + case 91: struct_len = 68; break; + default: return -1; + } + + + if (!buf) + { + len = 0; + if (uLevel > 0) len += StrlenExpanded(cnum,snum,lp_comment(snum)); + if (uLevel > 1) len += strlen(lp_pathname(snum)) + 1; + if (buflen) *buflen = struct_len; + if (stringspace) *stringspace = len; + return struct_len + len; + } + + len = struct_len; + p = *buf; + if ((*buflen) < struct_len) return -1; + if (stringbuf) + { + p2 = *stringbuf; + l2 = *stringspace; + } + else + { + p2 = p + struct_len; + l2 = (*buflen) - struct_len; + } + if (!baseaddr) baseaddr = p; + + StrnCpy(p,lp_servicename(snum),13); + + if (uLevel > 0) + { + int type; + CVAL(p,13) = 0; + type = STYPE_DISKTREE; + if (lp_print_ok(snum)) type = STYPE_PRINTQ; + if (strequal("IPC$",lp_servicename(snum))) type = STYPE_IPC; + SSVAL(p,14,type); /* device type */ + SIVAL(p,16,PTR_DIFF(p2,baseaddr)); + len += CopyExpanded(cnum,snum,&p2,lp_comment(snum),&l2); + } + + if (uLevel > 1) + { + SSVAL(p,20,ACCESS_READ|ACCESS_WRITE|ACCESS_CREATE); /* permissions */ + SSVALS(p,22,-1); /* max uses */ + SSVAL(p,24,1); /* current uses */ + SIVAL(p,26,PTR_DIFF(p2,baseaddr)); /* local pathname */ + len += CopyAndAdvance(&p2,lp_pathname(snum),&l2); + memset(p+30,0,SHPWLEN+2); /* passwd (reserved), pad field */ + } + + if (uLevel > 2) + { + memset(p+40,0,SHPWLEN+2); + SSVAL(p,50,0); + SIVAL(p,52,0); + SSVAL(p,56,0); + SSVAL(p,58,0); + SIVAL(p,60,0); + SSVAL(p,64,0); + SSVAL(p,66,0); + } + + if (stringbuf) + { + (*buf) = p + struct_len; + (*buflen) -= struct_len; + (*stringbuf) = p2; + (*stringspace) = l2; + } + else + { + (*buf) = p2; + (*buflen) -= len; + } + return len; +} + +static BOOL api_RNetShareGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *netname = skip_string(str2,1); + char *p = skip_string(netname,1); + int uLevel = SVAL(p,0); + int snum = find_service(netname); + + if (snum < 0) return False; + + /* check it's a supported varient */ + if (!prefix_ok(str1,"zWrLh")) return False; + if (!check_share_info(uLevel,str2)) return False; + + *rdata = REALLOC(*rdata,mdrcnt); + p = *rdata; + *rdata_len = fill_share_info(cnum,snum,uLevel,&p,&mdrcnt,0,0,0); + if (*rdata_len < 0) return False; + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + SSVAL(*rparam,4,*rdata_len); + + return(True); +} + +/**************************************************************************** + view list of shares available + ****************************************************************************/ +static BOOL api_RNetShareEnum(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + int buf_len = SVAL(p,2); + char *p2; + int count=lp_numservices(); + int total=0,counted=0; + int i; + int data_len, fixed_len, string_len; + int f_len, s_len; + + if (!prefix_ok(str1,"WrLeh")) return False; + if (!check_share_info(uLevel,str2)) return False; + + data_len = fixed_len = string_len = 0; + for (i=0;i<count;i++) + if (lp_browseable(i) && lp_snum_ok(i)) + { + total++; + data_len += fill_share_info(cnum,i,uLevel,0,&f_len,0,&s_len,0); + if (data_len <= buf_len) + { + counted++; + fixed_len += f_len; + string_len += s_len; + } + } + *rdata_len = fixed_len + string_len; + *rdata = REALLOC(*rdata,*rdata_len); + memset(*rdata,0,*rdata_len); + + p2 = (*rdata) + fixed_len; /* auxillery data (strings) will go here */ + p = *rdata; + f_len = fixed_len; + s_len = string_len; + for (i = 0; i < count;i++) + if (lp_browseable(i) && lp_snum_ok(i)) + if (fill_share_info(cnum,i,uLevel,&p,&f_len,&p2,&s_len,*rdata) < 0) + break; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,counted); + SSVAL(*rparam,6,total); + + DEBUG(3,("RNetShareEnum gave %d entries of %d (%d %d %d %d)\n", + counted,total,uLevel, + buf_len,*rdata_len,mdrcnt)); + return(True); +} + + + +/**************************************************************************** + get the time of day info + ****************************************************************************/ +static BOOL api_NetRemoteTOD(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *p; + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 21; + *rdata = REALLOC(*rdata,*rdata_len); + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + p = *rdata; + + { + struct tm *t; + time_t unixdate = time(NULL); + + put_dos_date3(p,0,unixdate); /* this is the time that is looked at + by NT in a "net time" operation, + it seems to ignore the one below */ + + /* the client expects to get localtime, not GMT, in this bit + (I think, this needs testing) */ + t = LocalTime(&unixdate,GMT_TO_LOCAL); + + SIVAL(p,4,0); /* msecs ? */ + CVAL(p,8) = t->tm_hour; + CVAL(p,9) = t->tm_min; + CVAL(p,10) = t->tm_sec; + CVAL(p,11) = 0; /* hundredths of seconds */ + SSVALS(p,12,TimeDiff(unixdate)/60); /* timezone in minutes from GMT */ + SSVAL(p,14,10000); /* timer interval in 0.0001 of sec */ + CVAL(p,16) = t->tm_mday; + CVAL(p,17) = t->tm_mon + 1; + SSVAL(p,18,1900+t->tm_year); + CVAL(p,20) = t->tm_wday; + } + + + return(True); +} + +/**************************************************************************** + set the user password + ****************************************************************************/ +static BOOL api_SetUserPassword(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *p = skip_string(param+2,2); + fstring user; + fstring pass1,pass2; + + strcpy(user,p); + + p = skip_string(p,1); + + StrnCpy(pass1,p,16); + StrnCpy(pass2,p+16,16); + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + DEBUG(3,("Set password for <%s>\n",user)); + + if (!password_ok(user,pass1,strlen(pass1),NULL,False) || + !chgpasswd(user,pass1,pass2)) + SSVAL(*rparam,0,NERR_badpass); + + bzero(pass1,sizeof(fstring)); + bzero(pass2,sizeof(fstring)); + + return(True); +} + +/**************************************************************************** + delete a print job + Form: <W> <> + ****************************************************************************/ +static BOOL api_RDosPrintJobDel(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + int function = SVAL(param,0); + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded + by the print queue api */ + int snum = (SVAL(p,0)>>8); + int i, count; + + + /* check it's a supported varient */ + if (!(strcsequal(str1,"W") && strcsequal(str2,""))) + return(False); + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_Success); + + if (snum >= 0 && VALID_SNUM(snum)) + { + print_queue_struct *queue=NULL; + lpq_reset(snum); + count = get_printqueue(snum,cnum,&queue,NULL); + + for (i=0;i<count;i++) + if ((queue[i].job%0xFF) == jobid) + { + switch (function) { + case 81: /* delete */ + DEBUG(3,("Deleting queue entry %d\n",queue[i].job)); + del_printqueue(cnum,snum,queue[i].job); + break; + case 82: /* pause */ + case 83: /* resume */ + DEBUG(3,("%s queue entry %d\n", + (function==82?"pausing":"resuming"),queue[i].job)); + status_printjob(cnum,snum,queue[i].job, + (function==82?LPQ_PAUSED:LPQ_QUEUED)); + break; + } + break; + } + + if (i==count) + SSVAL(*rparam,0,NERR_JobNotFound); + + if (queue) free(queue); + } + + SSVAL(*rparam,2,0); /* converter word */ + + return(True); +} + +static BOOL api_WPrintQueuePurge(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *QueueName = skip_string(str2,1); + int snum; + + /* check it's a supported varient */ + if (!(strcsequal(str1,"z") && strcsequal(str2,""))) + return(False); + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + snum = lp_servicenumber(QueueName); + if (snum < 0 && pcap_printername_ok(QueueName,NULL)) { + int pnum = lp_servicenumber(PRINTERS_NAME); + if (pnum >= 0) { + lp_add_printer(QueueName,pnum); + snum = lp_servicenumber(QueueName); + } + } + + if (snum >= 0 && VALID_SNUM(snum)) { + print_queue_struct *queue=NULL; + int i, count; + lpq_reset(snum); + + count = get_printqueue(snum,cnum,&queue,NULL); + for (i = 0; i < count; i++) + del_printqueue(cnum,snum,queue[i].job); + + if (queue) free(queue); + } + + DEBUG(3,("Print queue purge, queue=%s\n",QueueName)); + + return(True); +} + + +/**************************************************************************** + set the property of a print job (undocumented?) + ? function = 0xb -> set name of print job + ? function = 0x6 -> move print job up/down + Form: <WWsTP> <WWzWWDDzzzzzzzzzzlz> + or <WWsTP> <WB21BB16B10zWWzDDz> +****************************************************************************/ +static int check_printjob_info(struct pack_desc* desc, + int uLevel, char* id) +{ + desc->subformat = NULL; + switch( uLevel ) { + case 0: desc->format = "W"; break; + case 1: desc->format = "WB21BB16B10zWWzDDz"; break; + case 2: desc->format = "WWzWWDDzz"; break; + case 3: desc->format = "WWzWWDDzzzzzzzzzzlz"; break; + default: return False; + } + if (strcmp(desc->format,id) != 0) return False; + return True; +} + +static BOOL api_PrintJobInfo(int cnum,int uid,char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + struct pack_desc desc; + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int jobid = (SVAL(p,0)&0xFF); /* the snum and jobid are encoded + by the print queue api */ + int snum = (SVAL(p,0)>>8); + int uLevel = SVAL(p,2); + int function = SVAL(p,4); /* what is this ?? */ + int i; + char *s = data; + + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + /* check it's a supported varient */ + if ((strcmp(str1,"WWsTP")) || (!check_printjob_info(&desc,uLevel,str2))) + return(False); + + switch (function) { + case 0x6: /* change job place in the queue, data gives the new place */ + if (snum >= 0 && VALID_SNUM(snum)) + { + print_queue_struct *queue=NULL; + int count; + + lpq_reset(snum); + count = get_printqueue(snum,cnum,&queue,NULL); + for (i=0;i<count;i++) /* find job */ + if ((queue[i].job%0xFF) == jobid) break; + + if (i==count) { + desc.errcode=NERR_JobNotFound; + if (queue) free(queue); + } + else { + desc.errcode=NERR_Success; + i++; +#if 0 + { + int place= SVAL(data,0); + /* we currently have no way of doing this. Can any unix do it? */ + if (i < place) /* move down */; + else if (i > place ) /* move up */; + } +#endif + desc.errcode=NERR_notsupported; /* not yet supported */ + if (queue) free(queue); + } + } + else desc.errcode=NERR_JobNotFound; + break; + case 0xb: /* change print job name, data gives the name */ + /* jobid, snum should be zero */ + if (isalpha(*s)) + { + pstring name; + int l = 0; + while (l<64 && *s) + { + if (isalnum(*s) || strchr("-._",*s)) + name[l++] = *s; + s++; + } + name[l] = 0; + + DEBUG(3,("Setting print name to %s\n",name)); + + for (i=0;i<MAX_OPEN_FILES;i++) + if (Files[i].open && Files[i].print_file) + { + pstring wd; + GetWd(wd); + unbecome_user(); + + if (!become_user(Files[i].cnum,uid) || + !become_service(Files[i].cnum,True)) + break; + + if (sys_rename(Files[i].name,name) == 0) + string_set(&Files[i].name,name); + break; + } + } + desc.errcode=NERR_Success; + + break; + default: /* not implemented */ + return False; + } + + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); /* converter word */ + + return(True); +} + + +/**************************************************************************** + get info about the server + ****************************************************************************/ +static BOOL api_RNetServerGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel = SVAL(p,0); + char *p2; + int struct_len; + + DEBUG(4,("NetServerGetInfo level %d\n",uLevel)); + + /* check it's a supported varient */ + if (!prefix_ok(str1,"WrLh")) return False; + switch( uLevel ) { + case 0: + if (strcmp(str2,"B16") != 0) return False; + struct_len = 16; + break; + case 1: + if (strcmp(str2,"B16BBDz") != 0) return False; + struct_len = 26; + break; + case 2: + if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWz") + != 0) return False; + struct_len = 134; + break; + case 3: + if (strcmp(str2,"B16BBDzDDDWWzWWWWWWWBB21zWWWWWWWWWWWWWWWWWWWWWWzDWz") + != 0) return False; + struct_len = 144; + break; + case 20: + if (strcmp(str2,"DN") != 0) return False; + struct_len = 6; + break; + case 50: + if (strcmp(str2,"B16BBDzWWzzz") != 0) return False; + struct_len = 42; + break; + default: return False; + } + + *rdata_len = mdrcnt; + *rdata = REALLOC(*rdata,*rdata_len); + + p = *rdata; + p2 = p + struct_len; + if (uLevel != 20) { + StrnCpy(p,local_machine,16); + strupper(p); + } + p += 16; + if (uLevel > 0) + { + struct srv_info_struct *servers=NULL; + int i,count; + pstring comment; + uint32 servertype=SV_TYPE_SERVER_UNIX|SV_TYPE_WORKSTATION| + SV_TYPE_SERVER|SV_TYPE_TIME_SOURCE; + + strcpy(comment,lp_serverstring()); + + if ((count=get_server_info(SV_TYPE_ALL,&servers))>0) { + for (i=0;i<count;i++) + if (strequal(servers[i].name,local_machine)) { + servertype = servers[i].type; + strcpy(comment,servers[i].comment); + } + } + if (servers) free(servers); + + SCVAL(p,0,2); /* version_major */ + SCVAL(p,1,0); /* version_minor */ + SIVAL(p,2,servertype); + if (mdrcnt == struct_len) { + SIVAL(p,6,0); + } else { + SIVAL(p,6,PTR_DIFF(p2,*rdata)); + standard_sub(cnum,comment); + StrnCpy(p2,comment,MAX(mdrcnt - struct_len,0)); + p2 = skip_string(p2,1); + } + } + if (uLevel > 1) + { + return False; /* not yet implemented */ + } + + *rdata_len = PTR_DIFF(p2,*rdata); + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + SSVAL(*rparam,4,*rdata_len); + + return(True); +} + + +/**************************************************************************** + get info about the server + ****************************************************************************/ +static BOOL api_NetWkstaGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + char *p2; + extern pstring sesssetup_user; + int level = SVAL(p,0); + + DEBUG(4,("NetWkstaGetInfo level %d\n",level)); + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + + /* check it's a supported varient */ + if (!(level==10 && strcsequal(str1,"WrLh") && strcsequal(str2,"zzzBBzz"))) + return(False); + + *rdata_len = mdrcnt + 1024; + *rdata = REALLOC(*rdata,*rdata_len); + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + p = *rdata; + p2 = p + 22; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + strcpy(p2,local_machine); + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + strcpy(p2,sesssetup_user); + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + strcpy(p2,my_workgroup()); + p2 = skip_string(p2,1); + p += 4; + + SCVAL(p,0,2); /* major version?? */ + SCVAL(p,1,1); /* minor version?? */ + p += 2; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + strcpy(p2,my_workgroup()); /* login domain?? */ + p2 = skip_string(p2,1); + p += 4; + + SIVAL(p,0,PTR_DIFF(p2,*rdata)); + strcpy(p2,""); + p2 = skip_string(p2,1); + p += 4; + + *rdata_len = PTR_DIFF(p2,*rdata); + + SSVAL(*rparam,4,*rdata_len); + + return(True); +} + + +/**************************************************************************** + get info about a user + ****************************************************************************/ + +#define USER_PRIV_GUEST 0 +#define USER_PRIV_USER 1 +#define USER_PRIV_ADMIN 2 + +static BOOL api_RNetUserGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *UserName = skip_string(str2,1); + char *p = skip_string(UserName,1); + int uLevel = SVAL(p,0); + char *p2; + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + + /* check it's a supported varient */ + if (strcmp(str1,"zWrLh") != 0) return False; + switch( uLevel ) { + case 0: p2 = "B21"; break; + case 1: p2 = "B21BB16DWzzWz"; break; + case 2: p2 = "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"; break; + case 10: p2 = "B21Bzzz"; break; + case 11: p2 = "B21BzzzWDDzzDDWWzWzDWb21W"; break; + default: return False; + } + if (strcmp(p2,str2) != 0) return False; + + *rdata_len = mdrcnt + 1024; + *rdata = REALLOC(*rdata,*rdata_len); + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + p = *rdata; + p2 = p + 86; + + memset(p,0,21); + strcpy(p,UserName); + if (uLevel > 0) { + SCVAL(p,21,0); + *p2 = 0; + if (uLevel >= 10) { + SIVAL(p,22,PTR_DIFF(p2,p)); /* comment */ + strcpy(p2,"<Comment>"); + p2 = skip_string(p2,1); + SIVAL(p,26,PTR_DIFF(p2,p)); /* user_comment */ + strcpy(p2,"<UserComment>"); + p2 = skip_string(p2,1); + SIVAL(p,30,PTR_DIFF(p2,p)); /* full name */ + strcpy(p2,"<FullName>"); + p2 = skip_string(p2,1); + } + if (uLevel == 11) { /* modelled after NTAS 3.51 reply */ + SSVAL(p,34,USER_PRIV_USER); /* user privilege */ + SIVAL(p,36,0); /* auth flags */ + SIVALS(p,40,-1); /* password age */ + SIVAL(p,44,PTR_DIFF(p2,p)); /* home dir */ + strcpy(p2,"\\\\%L\\HOMES"); + standard_sub_basic(p2); + p2 = skip_string(p2,1); + SIVAL(p,48,PTR_DIFF(p2,p)); /* parms */ + strcpy(p2,""); + p2 = skip_string(p2,1); + SIVAL(p,52,0); /* last logon */ + SIVAL(p,56,0); /* last logoff */ + SSVALS(p,60,-1); /* bad pw counts */ + SSVALS(p,62,-1); /* num logons */ + SIVAL(p,64,PTR_DIFF(p2,p)); /* logon server */ + strcpy(p2,"\\\\*"); + p2 = skip_string(p2,1); + SSVAL(p,68,0); /* country code */ + + SIVAL(p,70,PTR_DIFF(p2,p)); /* workstations */ + strcpy(p2,""); + p2 = skip_string(p2,1); + + SIVALS(p,74,-1); /* max storage */ + SSVAL(p,78,168); /* units per week */ + SIVAL(p,80,PTR_DIFF(p2,p)); /* logon hours */ + memset(p2,-1,21); + SCVAL(p2,21,0); /* fix zero termination */ + p2 = skip_string(p2,1); + + SSVAL(p,84,0); /* code page */ + } + if (uLevel == 1 || uLevel == 2) { + memset(p+22,' ',16); /* password */ + SIVALS(p,38,-1); /* password age */ + SSVAL(p,42,USER_PRIV_ADMIN); /* user privilege */ + SIVAL(p,44,PTR_DIFF(p2,*rdata)); /* home dir */ + strcpy(p2,"\\\\%L\\HOMES"); + standard_sub_basic(p2); + p2 = skip_string(p2,1); + SIVAL(p,48,PTR_DIFF(p2,*rdata)); /* comment */ + *p2++ = 0; + SSVAL(p,52,0); /* flags */ + SIVAL(p,54,0); /* script_path */ + if (uLevel == 2) { + SIVAL(p,60,0); /* auth_flags */ + SIVAL(p,64,PTR_DIFF(p2,*rdata)); /* full_name */ + strcpy(p2,"<Full Name>"); + p2 = skip_string(p2,1); + SIVAL(p,68,0); /* urs_comment */ + SIVAL(p,72,PTR_DIFF(p2,*rdata)); /* parms */ + strcpy(p2,""); + p2 = skip_string(p2,1); + SIVAL(p,76,0); /* workstations */ + SIVAL(p,80,0); /* last_logon */ + SIVAL(p,84,0); /* last_logoff */ + SIVALS(p,88,-1); /* acct_expires */ + SIVALS(p,92,-1); /* max_storage */ + SSVAL(p,96,168); /* units_per_week */ + SIVAL(p,98,PTR_DIFF(p2,*rdata)); /* logon_hours */ + memset(p2,-1,21); + p2 += 21; + SSVALS(p,102,-1); /* bad_pw_count */ + SSVALS(p,104,-1); /* num_logons */ + SIVAL(p,106,PTR_DIFF(p2,*rdata)); /* logon_server */ + strcpy(p2,"\\\\%L"); + standard_sub_basic(p2); + p2 = skip_string(p2,1); + SSVAL(p,110,49); /* country_code */ + SSVAL(p,112,860); /* code page */ + } + } + } + + *rdata_len = PTR_DIFF(p2,*rdata); + + SSVAL(*rparam,4,*rdata_len); /* is this right?? */ + + return(True); +} + + +/******************************************************************* + get groups that a user is a member of + ******************************************************************/ +static BOOL api_NetUserGetGroups(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *UserName = skip_string(str2,1); + char *p = skip_string(UserName,1); + int uLevel = SVAL(p,0); + char *p2; + int count=0; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + + /* check it's a supported varient */ + if (strcmp(str1,"zWrLeh") != 0) return False; + switch( uLevel ) { + case 0: p2 = "B21"; break; + default: return False; + } + if (strcmp(p2,str2) != 0) return False; + + *rdata_len = mdrcnt + 1024; + *rdata = REALLOC(*rdata,*rdata_len); + + SSVAL(*rparam,0,NERR_Success); + SSVAL(*rparam,2,0); /* converter word */ + + p = *rdata; + + /* XXXX we need a real SAM database some day */ + strcpy(p,"Users"); p += 21; count++; + strcpy(p,"Domain Users"); p += 21; count++; + strcpy(p,"Guests"); p += 21; count++; + strcpy(p,"Domain Guests"); p += 21; count++; + + *rdata_len = PTR_DIFF(p,*rdata); + + SSVAL(*rparam,4,count); /* is this right?? */ + SSVAL(*rparam,6,count); /* is this right?? */ + + return(True); +} + + +static BOOL api_WWkstaUserLogon(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel; + struct pack_desc desc; + char* name; + + uLevel = SVAL(p,0); + name = p + 2; + + bzero(&desc,sizeof(desc)); + + DEBUG(3,("WWkstaUserLogon uLevel=%d name=%s\n",uLevel,name)); + + /* check it's a supported varient */ + if (strcmp(str1,"OOWb54WrLh") != 0) return False; + if (uLevel != 1 || strcmp(str2,"WB21BWDWWDDDDDDDzzzD") != 0) return False; + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + desc.subformat = NULL; + desc.format = str2; + + + + if (init_package(&desc,1,0)) { + PACKI(&desc,"W",0); /* code */ + PACKS(&desc,"B21",name); /* eff. name */ + PACKS(&desc,"B",""); /* pad */ + PACKI(&desc,"W", + Connections[cnum].admin_user?USER_PRIV_ADMIN:USER_PRIV_USER); + PACKI(&desc,"D",0); /* auth flags XXX */ + PACKI(&desc,"W",0); /* num logons */ + PACKI(&desc,"W",0); /* bad pw count */ + PACKI(&desc,"D",-1); /* last logon */ + PACKI(&desc,"D",-1); /* last logoff */ + PACKI(&desc,"D",-1); /* logoff time */ + PACKI(&desc,"D",-1); /* kickoff time */ + PACKI(&desc,"D",0); /* password age */ + PACKI(&desc,"D",0); /* password can change */ + PACKI(&desc,"D",-1); /* password must change */ + { + fstring mypath; + strcpy(mypath,"\\\\"); + strcat(mypath,local_machine); + strupper(mypath); + PACKS(&desc,"z",mypath); /* computer */ + } + PACKS(&desc,"z",my_workgroup());/* domain */ + PACKS(&desc,"z",lp_logon_script()); /* script path */ + PACKI(&desc,"D",0); /* reserved */ + } + + *rdata_len = desc.usedlen; + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,desc.neededlen); + + DEBUG(4,("WWkstaUserLogon: errorcode %d\n",desc.errcode)); + return(True); +} + + +/**************************************************************************** + api_WAccessGetUserPerms + ****************************************************************************/ +static BOOL api_WAccessGetUserPerms(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *user = skip_string(str2,1); + char *resource = skip_string(user,1); + + DEBUG(3,("WAccessGetUserPerms user=%s resource=%s\n",user,resource)); + + /* check it's a supported varient */ + if (strcmp(str1,"zzh") != 0) return False; + if (strcmp(str2,"") != 0) return False; + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,0); /* errorcode */ + SSVAL(*rparam,2,0); /* converter word */ + SSVAL(*rparam,4,0x7f); /* permission flags */ + + return(True); +} + +/**************************************************************************** + api_WPrintJobEnumerate + ****************************************************************************/ +static BOOL api_WPrintJobGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uJobId = SVAL(p,0); + int uLevel,cbBuf; + int count; + int i; + int snum; + int job; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + + uLevel = SVAL(p,2); + cbBuf = SVAL(p,4); + + bzero(&desc,sizeof(desc)); + bzero(&status,sizeof(status)); + + DEBUG(3,("WPrintJobGetInfo uLevel=%d uJobId=0x%X\n",uLevel,uJobId)); + + /* check it's a supported varient */ + if (strcmp(str1,"WWrLh") != 0) return False; + if (!check_printjob_info(&desc,uLevel,str2)) return False; + + snum = (unsigned int)uJobId >> 8; /*## valid serice number??*/ + job = uJobId & 0xFF; + + if (snum < 0 || !VALID_SNUM(snum)) return(False); + + count = get_printqueue(snum,cnum,&queue,&status); + for (i = 0; i < count; i++) { + if ((queue[i].job % 0xFF) == job) break; + } + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + + if (init_package(&desc,1,0)) { + if (i < count) { + fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i); + *rdata_len = desc.usedlen; + } + else { + desc.errcode = NERR_JobNotFound; + *rdata_len = 0; + } + } + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,desc.neededlen); + + if (queue) free(queue); + + DEBUG(4,("WPrintJobGetInfo: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintJobEnumerate(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + char* name = p; + int uLevel,cbBuf; + int count; + int i, succnt=0; + int snum; + struct pack_desc desc; + print_queue_struct *queue=NULL; + print_status_struct status; + + bzero(&desc,sizeof(desc)); + bzero(&status,sizeof(status)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintJobEnumerate uLevel=%d name=%s\n",uLevel,name)); + + /* check it's a supported varient */ + if (strcmp(str1,"zWrLeh") != 0) return False; + if (uLevel > 2) return False; /* defined only for uLevel 0,1,2 */ + if (!check_printjob_info(&desc,uLevel,str2)) return False; + + snum = lp_servicenumber(name); + if (snum < 0 && pcap_printername_ok(name,NULL)) { + int pnum = lp_servicenumber(PRINTERS_NAME); + if (pnum >= 0) { + lp_add_printer(name,pnum); + snum = lp_servicenumber(name); + } + } + + if (snum < 0 || !VALID_SNUM(snum)) return(False); + + count = get_printqueue(snum,cnum,&queue,&status); + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + + if (init_package(&desc,count,0)) { + succnt = 0; + for (i = 0; i < count; i++) { + fill_printjob_info(cnum,snum,uLevel,&desc,&queue[i],i); + if (desc.errcode == NERR_Success) succnt = i+1; + } + } + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,count); + + if (queue) free(queue); + + DEBUG(4,("WPrintJobEnumerate: errorcode %d\n",desc.errcode)); + return(True); +} + +static int check_printdest_info(struct pack_desc* desc, + int uLevel, char* id) +{ + desc->subformat = NULL; + switch( uLevel ) { + case 0: desc->format = "B9"; break; + case 1: desc->format = "B9B21WWzW"; break; + case 2: desc->format = "z"; break; + case 3: desc->format = "zzzWWzzzWW"; break; + default: return False; + } + if (strcmp(desc->format,id) != 0) return False; + return True; +} + +static void fill_printdest_info(int cnum, int snum, int uLevel, + struct pack_desc* desc) +{ + char buf[100]; + strcpy(buf,SERVICE(snum)); + strupper(buf); + if (uLevel <= 1) { + PACKS(desc,"B9",buf); /* szName */ + if (uLevel == 1) { + PACKS(desc,"B21",""); /* szUserName */ + PACKI(desc,"W",0); /* uJobId */ + PACKI(desc,"W",0); /* fsStatus */ + PACKS(desc,"z",""); /* pszStatus */ + PACKI(desc,"W",0); /* time */ + } + } + if (uLevel == 2 || uLevel == 3) { + PACKS(desc,"z",buf); /* pszPrinterName */ + if (uLevel == 3) { + PACKS(desc,"z",""); /* pszUserName */ + PACKS(desc,"z",""); /* pszLogAddr */ + PACKI(desc,"W",0); /* uJobId */ + PACKI(desc,"W",0); /* fsStatus */ + PACKS(desc,"z",""); /* pszStatus */ + PACKS(desc,"z",""); /* pszComment */ + PACKS(desc,"z","NULL"); /* pszDrivers */ + PACKI(desc,"W",0); /* time */ + PACKI(desc,"W",0); /* pad1 */ + } + } +} + +static BOOL api_WPrintDestGetInfo(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + char* PrinterName = p; + int uLevel,cbBuf; + struct pack_desc desc; + int snum; + + bzero(&desc,sizeof(desc)); + + p = skip_string(p,1); + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintDestGetInfo uLevel=%d PrinterName=%s\n",uLevel,PrinterName)); + + /* check it's a supported varient */ + if (strcmp(str1,"zWrLh") != 0) return False; + if (!check_printdest_info(&desc,uLevel,str2)) return False; + + snum = lp_servicenumber(PrinterName); + if (snum < 0 && pcap_printername_ok(PrinterName,NULL)) { + int pnum = lp_servicenumber(PRINTERS_NAME); + if (pnum >= 0) { + lp_add_printer(PrinterName,pnum); + snum = lp_servicenumber(PrinterName); + } + } + + if (snum < 0) { + *rdata_len = 0; + desc.errcode = NERR_DestNotFound; + desc.neededlen = 0; + } + else { + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + if (init_package(&desc,1,0)) { + fill_printdest_info(cnum,snum,uLevel,&desc); + } + *rdata_len = desc.usedlen; + } + + *rparam_len = 6; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,desc.neededlen); + + DEBUG(4,("WPrintDestGetInfo: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintDestEnum(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel,cbBuf; + int queuecnt; + int i, n, succnt=0; + struct pack_desc desc; + int services = lp_numservices(); + + bzero(&desc,sizeof(desc)); + + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintDestEnum uLevel=%d\n",uLevel)); + + /* check it's a supported varient */ + if (strcmp(str1,"WrLeh") != 0) return False; + if (!check_printdest_info(&desc,uLevel,str2)) return False; + + queuecnt = 0; + for (i = 0; i < services; i++) + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) + queuecnt++; + + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + if (init_package(&desc,queuecnt,0)) { + succnt = 0; + n = 0; + for (i = 0; i < services; i++) { + if (lp_snum_ok(i) && lp_print_ok(i) && lp_browseable(i)) { + fill_printdest_info(cnum,i,uLevel,&desc); + n++; + if (desc.errcode == NERR_Success) succnt = n; + } + } + } + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,queuecnt); + + DEBUG(4,("WPrintDestEnumerate: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintDriverEnum(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel,cbBuf; + int succnt; + struct pack_desc desc; + + bzero(&desc,sizeof(desc)); + + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintDriverEnum uLevel=%d\n",uLevel)); + + /* check it's a supported varient */ + if (strcmp(str1,"WrLeh") != 0) return False; + if (uLevel != 0 || strcmp(str2,"B41") != 0) return False; + + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + if (init_package(&desc,1,0)) { + PACKS(&desc,"B41","NULL"); + } + + succnt = (desc.errcode == NERR_Success ? 1 : 0); + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,1); + + DEBUG(4,("WPrintDriverEnum: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintQProcEnum(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel,cbBuf; + int succnt; + struct pack_desc desc; + + bzero(&desc,sizeof(desc)); + + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintQProcEnum uLevel=%d\n",uLevel)); + + /* check it's a supported varient */ + if (strcmp(str1,"WrLeh") != 0) return False; + if (uLevel != 0 || strcmp(str2,"B13") != 0) return False; + + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + desc.base = *rdata; + desc.buflen = mdrcnt; + desc.format = str2; + if (init_package(&desc,1,0)) { + PACKS(&desc,"B13","lpd"); + } + + succnt = (desc.errcode == NERR_Success ? 1 : 0); + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,1); + + DEBUG(4,("WPrintQProcEnum: errorcode %d\n",desc.errcode)); + return(True); +} + +static BOOL api_WPrintPortEnum(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + char *str1 = param+2; + char *str2 = skip_string(str1,1); + char *p = skip_string(str2,1); + int uLevel,cbBuf; + int succnt; + struct pack_desc desc; + + bzero(&desc,sizeof(desc)); + + uLevel = SVAL(p,0); + cbBuf = SVAL(p,2); + + DEBUG(3,("WPrintPortEnum uLevel=%d\n",uLevel)); + + /* check it's a supported varient */ + if (strcmp(str1,"WrLeh") != 0) return False; + if (uLevel != 0 || strcmp(str2,"B9") != 0) return False; + + if (mdrcnt > 0) *rdata = REALLOC(*rdata,mdrcnt); + bzero(&desc,sizeof(desc)); + desc.base = *rdata; + desc.buflen = mdrcnt; + desc.format = str2; + if (init_package(&desc,1,0)) { + PACKS(&desc,"B13","lp0"); + } + + succnt = (desc.errcode == NERR_Success ? 1 : 0); + + *rdata_len = desc.usedlen; + + *rparam_len = 8; + *rparam = REALLOC(*rparam,*rparam_len); + SSVALS(*rparam,0,desc.errcode); + SSVAL(*rparam,2,0); + SSVAL(*rparam,4,succnt); + SSVAL(*rparam,6,1); + + DEBUG(4,("WPrintPortEnum: errorcode %d\n",desc.errcode)); + return(True); +} + +/**************************************************************************** + the buffer was too small + ****************************************************************************/ +static BOOL api_TooSmall(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + *rparam_len = MIN(*rparam_len,mprcnt); + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_BufTooSmall); + + DEBUG(3,("Supplied buffer too small in API command\n")); + + return(True); +} + + +/**************************************************************************** + the request is not supported + ****************************************************************************/ +static BOOL api_Unsupported(int cnum,int uid, char *param,char *data, + int mdrcnt,int mprcnt, + char **rdata,char **rparam, + int *rdata_len,int *rparam_len) +{ + *rparam_len = 4; + *rparam = REALLOC(*rparam,*rparam_len); + + *rdata_len = 0; + + SSVAL(*rparam,0,NERR_notsupported); + SSVAL(*rparam,2,0); /* converter word */ + + DEBUG(3,("Unsupported API command\n")); + + return(True); +} + + + + +struct +{ + char *name; + int id; + BOOL (*fn)(); + int flags; +} api_commands[] = { + {"RNetShareEnum", 0, api_RNetShareEnum,0}, + {"RNetShareGetInfo", 1, api_RNetShareGetInfo,0}, + {"RNetServerGetInfo", 13, api_RNetServerGetInfo,0}, + {"RNetUserGetInfo", 56, api_RNetUserGetInfo,0}, + {"NetUserGetGroups", 59, api_NetUserGetGroups,0}, + {"NetWkstaGetInfo", 63, api_NetWkstaGetInfo,0}, + {"DosPrintQEnum", 69, api_DosPrintQEnum,0}, + {"DosPrintQGetInfo", 70, api_DosPrintQGetInfo,0}, + {"WPrintJobEnumerate",76, api_WPrintJobEnumerate,0}, + {"WPrintJobGetInfo", 77, api_WPrintJobGetInfo,0}, + {"RDosPrintJobDel", 81, api_RDosPrintJobDel,0}, + {"RDosPrintJobPause", 82, api_RDosPrintJobDel,0}, + {"RDosPrintJobResume",83, api_RDosPrintJobDel,0}, + {"WPrintDestEnum", 84, api_WPrintDestEnum,0}, + {"WPrintDestGetInfo", 85, api_WPrintDestGetInfo,0}, + {"NetRemoteTOD", 91, api_NetRemoteTOD,0}, + {"WPrintQueuePurge", 103, api_WPrintQueuePurge,0}, + {"NetServerEnum", 104, api_RNetServerEnum,0}, + {"WAccessGetUserPerms",105, api_WAccessGetUserPerms,0}, + {"SetUserPassword", 115, api_SetUserPassword,0}, + {"WWkstaUserLogon", 132, api_WWkstaUserLogon,0}, + {"PrintJobInfo", 147, api_PrintJobInfo,0}, + {"WPrintDriverEnum", 205, api_WPrintDriverEnum,0}, + {"WPrintQProcEnum", 206, api_WPrintQProcEnum,0}, + {"WPrintPortEnum", 207, api_WPrintPortEnum,0}, + {NULL, -1, api_Unsupported,0}}; + + +/**************************************************************************** + handle remote api calls + ****************************************************************************/ +static int api_reply(int cnum,int uid,char *outbuf,char *data,char *params, + int tdscnt,int tpscnt,int mdrcnt,int mprcnt) +{ + int api_command = SVAL(params,0); + char *rdata = NULL; + char *rparam = NULL; + int rdata_len = 0; + int rparam_len = 0; + BOOL reply=False; + int i; + + DEBUG(3,("Got API command %d of form <%s> <%s> (tdscnt=%d,tpscnt=%d,mdrcnt=%d,mprcnt=%d)\n", + api_command,params+2,skip_string(params+2,1), + tdscnt,tpscnt,mdrcnt,mprcnt)); + + for (i=0;api_commands[i].name;i++) + if (api_commands[i].id == api_command && api_commands[i].fn) + { + DEBUG(3,("Doing %s\n",api_commands[i].name)); + break; + } + + rdata = (char *)malloc(1024); if (rdata) bzero(rdata,1024); + rparam = (char *)malloc(1024); if (rparam) bzero(rparam,1024); + + reply = api_commands[i].fn(cnum,uid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + + + if (rdata_len > mdrcnt || + rparam_len > mprcnt) + { + reply = api_TooSmall(cnum,uid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + } + + + /* if we get False back then it's actually unsupported */ + if (!reply) + api_Unsupported(cnum,uid,params,data,mdrcnt,mprcnt, + &rdata,&rparam,&rdata_len,&rparam_len); + + + + /* now send the reply */ + send_trans_reply(outbuf,rdata,rparam,NULL,rdata_len,rparam_len,0); + + if (rdata) + free(rdata); + if (rparam) + free(rparam); + + return(-1); +} + +/**************************************************************************** + handle named pipe commands + ****************************************************************************/ +static int named_pipe(int cnum,int uid, char *outbuf,char *name, + uint16 *setup,char *data,char *params, + int suwcnt,int tdscnt,int tpscnt, + int msrcnt,int mdrcnt,int mprcnt) +{ + + if (strequal(name,"LANMAN")) + return(api_reply(cnum,uid,outbuf,data,params,tdscnt,tpscnt,mdrcnt,mprcnt)); + + DEBUG(3,("named pipe command on <%s> 0x%X setup1=%d\n", + name,(int)setup[0],(int)setup[1])); + + return(0); +} + + +/**************************************************************************** + reply to a SMBtrans + ****************************************************************************/ +int reply_trans(char *inbuf,char *outbuf) +{ + fstring name; + + char *data=NULL,*params=NULL; + uint16 *setup=NULL; + + int outsize = 0; + int cnum = SVAL(inbuf,smb_tid); + int uid = SVAL(inbuf,smb_uid); + + int tpscnt = SVAL(inbuf,smb_vwv0); + int tdscnt = SVAL(inbuf,smb_vwv1); + int mprcnt = SVAL(inbuf,smb_vwv2); + int mdrcnt = SVAL(inbuf,smb_vwv3); + int msrcnt = CVAL(inbuf,smb_vwv4); + BOOL close_on_completion = BITSETW(inbuf+smb_vwv5,0); + BOOL one_way = BITSETW(inbuf+smb_vwv5,1); + int pscnt = SVAL(inbuf,smb_vwv9); + int psoff = SVAL(inbuf,smb_vwv10); + int dscnt = SVAL(inbuf,smb_vwv11); + int dsoff = SVAL(inbuf,smb_vwv12); + int suwcnt = CVAL(inbuf,smb_vwv13); + + StrnCpy(name,smb_buf(inbuf),sizeof(name)-1); + + if (tdscnt) + { + data = (char *)malloc(tdscnt); + memcpy(data,smb_base(inbuf)+dsoff,dscnt); + } + if (tpscnt) + { + params = (char *)malloc(tpscnt); + memcpy(params,smb_base(inbuf)+psoff,pscnt); + } + + if (suwcnt) + { + int i; + setup = (uint16 *)malloc(suwcnt*sizeof(setup[0])); + for (i=0;i<suwcnt;i++) + setup[i] = SVAL(inbuf,smb_vwv14+i*SIZEOFWORD); + } + + + if (pscnt < tpscnt || dscnt < tdscnt) + { + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + show_msg(outbuf); + send_smb(Client,outbuf); + } + + /* receive the rest of the trans packet */ + while (pscnt < tpscnt || dscnt < tdscnt) + { + int pcnt,poff,dcnt,doff,pdisp,ddisp; + + receive_smb(Client,inbuf, 0); + show_msg(inbuf); + + /* Ensure this is still a trans packet (sanity check) */ + if(CVAL(inbuf, smb_com) != SMBtrans) + { + DEBUG(2,("Invalid secondary trans2 packet\n")); + if (params) free(params); + if (data) free(data); + if (setup) free(setup); + return(ERROR(ERRSRV,ERRerror)); + } + + tpscnt = SVAL(inbuf,smb_vwv0); + tdscnt = SVAL(inbuf,smb_vwv1); + + pcnt = SVAL(inbuf,smb_vwv2); + poff = SVAL(inbuf,smb_vwv3); + pdisp = SVAL(inbuf,smb_vwv4); + + dcnt = SVAL(inbuf,smb_vwv5); + doff = SVAL(inbuf,smb_vwv6); + ddisp = SVAL(inbuf,smb_vwv7); + + pscnt += pcnt; + dscnt += dcnt; + + if (pcnt) + memcpy(params+pdisp,smb_base(inbuf)+poff,pcnt); + if (dcnt) + memcpy(data+ddisp,smb_base(inbuf)+doff,dcnt); + } + + + DEBUG(3,("trans <%s> data=%d params=%d setup=%d\n",name,tdscnt,tpscnt,suwcnt)); + + + if (strncmp(name,"\\PIPE\\",strlen("\\PIPE\\")) == 0) + outsize = named_pipe(cnum,uid,outbuf,name+strlen("\\PIPE\\"),setup,data,params, + suwcnt,tdscnt,tpscnt,msrcnt,mdrcnt,mprcnt); + + + if (data) free(data); + if (params) free(params); + if (setup) free(setup); + + if (close_on_completion) + close_cnum(cnum,uid); + + if (one_way) + return(-1); + + if (outsize == 0) + return(ERROR(ERRSRV,ERRnosupport)); + + return(outsize); +} + + diff --git a/source3/smbd/mangle.c b/source3/smbd/mangle.c new file mode 100644 index 0000000000..8f1490c528 --- /dev/null +++ b/source3/smbd/mangle.c @@ -0,0 +1,610 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Name mangling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" + +extern int DEBUGLEVEL; +extern int case_default; +extern BOOL case_mangle; + +/**************************************************************************** +provide a checksum on a string +****************************************************************************/ +int str_checksum(char *s) +{ + int res = 0; + int c; + int i=0; + while (*s) + { + c = *s; + res ^= (c << (i % 15)) ^ (c >> (15-(i%15))); + s++; i++; + } + return(res); +} + +/**************************************************************************** +return True if a name is a special msdos reserved name +****************************************************************************/ +static BOOL is_reserved_msdos(char *fname) +{ + char upperFname[13]; + char *p; + + StrnCpy (upperFname, fname, 12); + + /* lpt1.txt and con.txt etc are also illegal */ + p=strchr(upperFname,'.'); + if (p) + *p='\0'; + strupper (upperFname); + if ((strcmp(upperFname,"CLOCK$") == 0) || + (strcmp(upperFname,"CON") == 0) || + (strcmp(upperFname,"AUX") == 0) || + (strcmp(upperFname,"COM1") == 0) || + (strcmp(upperFname,"COM2") == 0) || + (strcmp(upperFname,"COM3") == 0) || + (strcmp(upperFname,"COM4") == 0) || + (strcmp(upperFname,"LPT1") == 0) || + (strcmp(upperFname,"LPT2") == 0) || + (strcmp(upperFname,"LPT3") == 0) || + (strcmp(upperFname,"NUL") == 0) || + (strcmp(upperFname,"PRN") == 0)) + return (True) ; + + return (False); +} + + + +/**************************************************************************** +return True if a name is in 8.3 dos format +****************************************************************************/ +BOOL is_8_3(char *fname) +{ + int len; + char *dot_pos; + char *slash_pos = strrchr(fname,'/'); + int l; + + if (slash_pos) fname = slash_pos+1; + len = strlen(fname); + + dot_pos = strchr(fname,'.'); + + DEBUG(5,("checking %s for 8.3\n",fname)); + + if (case_mangle) + switch (case_default) + { + case CASE_LOWER: + if (strhasupper(fname)) return(False); + break; + case CASE_UPPER: + if (strhaslower(fname)) return(False); + break; + } + + /* can't be longer than 12 chars */ + if (len == 0 || len > 12) + return(False); + + /* can't be an MS-DOS Special file such as lpt1 or even lpt1.txt */ + if (is_reserved_msdos(fname)) + return(False); + + /* can't contain invalid dos chars */ + /* Windows use the ANSI charset. + But filenames are translated in the PC charset. + This Translation may be more or less relaxed depending + the Windows application. */ + + /* %%% A nice improvment to name mangling would be to translate + filename to ANSI charset on the smb server host */ + + { + char *p = fname; +#ifdef KANJI + dot_pos = 0; + while (*p) + { + if (is_shift_jis (*p)) { + p += 2; + } else if (is_kana (*p)) { + p ++; + } else { + if (*p == '.' && !dot_pos) + dot_pos = (char *) p; + if (!isdoschar(*p)) + return(False); + p++; + } + } +#else + while (*p) + { + if (!isdoschar(*p)) + return(False); + p++; + } +#endif /* KANJI */ + } + + /* no dot and less than 9 means OK */ + if (!dot_pos) + return(len <= 8); + + l = PTR_DIFF(dot_pos,fname); + + /* base must be at least 1 char except special cases . and .. */ + if (l == 0) + return(strcmp(fname,".") == 0 || strcmp(fname,"..") == 0); + + /* base can't be greater than 8 */ + if (l > 8) + return(False); + + if (lp_strip_dot() && + len - l == 1 && + !strchr(dot_pos+1,'.')) + { + *dot_pos = 0; + return(True); + } + + /* extension must be between 1 and 3 */ + if ( (len - l < 2 ) || (len - l > 4) ) + return(False); + + /* extension can't have a dot */ + if (strchr(dot_pos+1,'.')) + return(False); + + /* must be in 8.3 format */ + return(True); +} + + + +/* +keep a stack of name mangling results - just +so file moves and copies have a chance of working +*/ +fstring *mangled_stack = NULL; +int mangled_stack_size = 0; +int mangled_stack_len = 0; + +/**************************************************************************** +create the mangled stack +****************************************************************************/ +void create_mangled_stack(int size) +{ + if (mangled_stack) + { + free(mangled_stack); + mangled_stack_size = 0; + mangled_stack_len = 0; + } + if (size > 0) + mangled_stack = (fstring *)malloc(sizeof(fstring)*size); + if (mangled_stack) mangled_stack_size = size; +} + +/**************************************************************************** +push a mangled name onto the stack +****************************************************************************/ +static void push_mangled_name(char *s) +{ + int i; + char *p; + + if (!mangled_stack) + return; + + for (i=0;i<mangled_stack_len;i++) + if (strcmp(s,mangled_stack[i]) == 0) + { + array_promote(mangled_stack[0],sizeof(fstring),i); + return; + } + + memmove(mangled_stack[1],mangled_stack[0], + sizeof(fstring)*MIN(mangled_stack_len,mangled_stack_size-1)); + strcpy(mangled_stack[0],s); + p = strrchr(mangled_stack[0],'.'); + if (p && (!strhasupper(p+1)) && (strlen(p+1) < 4)) + *p = 0; + mangled_stack_len = MIN(mangled_stack_size,mangled_stack_len+1); +} + +/**************************************************************************** +check for a name on the mangled name stack +****************************************************************************/ +BOOL check_mangled_stack(char *s) +{ + int i; + pstring tmpname; + char extension[5]; + char *p = strrchr(s,'.'); + BOOL check_extension = False; + + extension[0] = 0; + + if (!mangled_stack) return(False); + + if (p) + { + check_extension = True; + StrnCpy(extension,p,4); + strlower(extension); /* XXXXXXX */ + } + + for (i=0;i<mangled_stack_len;i++) + { + strcpy(tmpname,mangled_stack[i]); + mangle_name_83(tmpname); + if (strequal(tmpname,s)) + { + strcpy(s,mangled_stack[i]); + break; + } + if (check_extension && !strchr(mangled_stack[i],'.')) + { + strcpy(tmpname,mangled_stack[i]); + strcat(tmpname,extension); + mangle_name_83(tmpname); + if (strequal(tmpname,s)) + { + strcpy(s,mangled_stack[i]); + strcat(s,extension); + break; + } + } + } + + if (i < mangled_stack_len) + { + DEBUG(3,("Found %s on mangled stack as %s\n",s,mangled_stack[i])); + array_promote(mangled_stack[0],sizeof(fstring),i); + return(True); + } + + return(False); +} + +static char *map_filename(char *s, /* This is null terminated */ + char *pattern, /* This isn't. */ + int len) /* This is the length of pattern. */ +{ + static pstring matching_bit; /* The bit of the string which matches */ + /* a * in pattern if indeed there is a * */ + char *sp; /* Pointer into s. */ + char *pp; /* Pointer into p. */ + char *match_start; /* Where the matching bit starts. */ + pstring pat; + + StrnCpy(pat, pattern, len); /* Get pattern into a proper string! */ + strcpy(matching_bit,""); /* Match but no star gets this. */ + pp = pat; /* Initialise the pointers. */ + sp = s; + if ((len == 1) && (*pattern == '*')) { + return NULL; /* Impossible, too ambiguous for */ + /* words! */ + } + + while ((*sp) /* Not the end of the string. */ + && (*pp) /* Not the end of the pattern. */ + && (*sp == *pp) /* The two match. */ + && (*pp != '*')) { /* No wildcard. */ + sp++; /* Keep looking. */ + pp++; + } + if (!*sp && !*pp) /* End of pattern. */ + return matching_bit; /* Simple match. Return empty string. */ + if (*pp == '*') { + pp++; /* Always interrested in the chacter */ + /* after the '*' */ + if (!*pp) { /* It is at the end of the pattern. */ + StrnCpy(matching_bit, s, sp-s); + return matching_bit; + } else { + /* The next character in pattern must match a character further */ + /* along s than sp so look for that character. */ + match_start = sp; + while ((*sp) /* Not the end of s. */ + && (*sp != *pp)) /* Not the same */ + sp++; /* Keep looking. */ + if (!*sp) { /* Got to the end without a match. */ + return NULL; + } else { /* Still hope for a match. */ + /* Now sp should point to a matching character. */ + StrnCpy(matching_bit, match_start, sp-match_start); + /* Back to needing a stright match again. */ + while ((*sp) /* Not the end of the string. */ + && (*pp) /* Not the end of the pattern. */ + && (*sp == *pp)) { /* The two match. */ + sp++; /* Keep looking. */ + pp++; + } + if (!*sp && !*pp) /* Both at end so it matched */ + return matching_bit; + else + return NULL; + } + } + } + return NULL; /* No match. */ +} + + +/* this is the magic char used for mangling */ +char magic_char = '~'; + + +/**************************************************************************** +determine whther is name could be a mangled name +****************************************************************************/ +BOOL is_mangled(char *s) +{ + char *m = strchr(s,magic_char); + if (!m) return(False); + + /* we use two base 36 chars efore the extension */ + if (m[1] == '.' || m[1] == 0 || + m[2] == '.' || m[2] == 0 || + (m[3] != '.' && m[3] != 0)) + return(is_mangled(m+1)); + + /* it could be */ + return(True); +} + + + +/**************************************************************************** +return a base 36 character. v must be from 0 to 35. +****************************************************************************/ +static char base36(int v) +{ + v = v % 36; + if (v < 10) + return('0'+v); + else /* needed to work around a DEC C compiler bug */ + return('A' + (v-10)); +} + + +static void do_fwd_mangled_map(char *s, char *MangledMap) +{ + /* MangledMap is a series of name pairs in () separated by spaces. + * If s matches the first of the pair then the name given is the + * second of the pair. A * means any number of any character and if + * present in the second of the pair as well as the first the + * matching part of the first string takes the place of the * in the + * second. + * + * I wanted this so that we could have RCS files which can be used + * by UNIX and DOS programs. My mapping string is (RCS rcs) which + * converts the UNIX RCS file subdirectory to lowercase thus + * preventing mangling. + */ + char *start=MangledMap; /* Use this to search for mappings. */ + char *end; /* Used to find the end of strings. */ + char *match_string; + pstring new_string; /* Make up the result here. */ + char *np; /* Points into new_string. */ + + DEBUG(5,("Mangled Mapping '%s' map '%s'\n", s, MangledMap)); + while (*start) { + while ((*start) && (*start != '(')) + start++; + start++; /* Skip the ( */ + if (!*start) + continue; /* Always check for the end. */ + end = start; /* Search for the ' ' or a ')' */ + DEBUG(5,("Start of first in pair '%s'\n", start)); + while ((*end) && !((*end == ' ') || (*end == ')'))) + end++; + if (!*end) { + start = end; + continue; /* Always check for the end. */ + } + DEBUG(5,("End of first in pair '%s'\n", end)); + if ((match_string = map_filename(s, start, end-start))) { + DEBUG(5,("Found a match\n")); + /* Found a match. */ + start = end+1; /* Point to start of what it is to become. */ + DEBUG(5,("Start of second in pair '%s'\n", start)); + end = start; + np = new_string; + while ((*end) /* Not the end of string. */ + && (*end != ')') /* Not the end of the pattern. */ + && (*end != '*')) /* Not a wildcard. */ + *np++ = *end++; + if (!*end) { + start = end; + continue; /* Always check for the end. */ + } + if (*end == '*') { + strcpy(np, match_string); + np += strlen(match_string); + end++; /* Skip the '*' */ + while ((*end) /* Not the end of string. */ + && (*end != ')') /* Not the end of the pattern. */ + && (*end != '*')) /* Not a wildcard. */ + *np++ = *end++; + } + if (!*end) { + start = end; + continue; /* Always check for the end. */ + } + *np++ = '\0'; /* NULL terminate it. */ + DEBUG(5,("End of second in pair '%s'\n", end)); + strcpy(s, new_string); /* Substitute with the new name. */ + DEBUG(5,("s is now '%s'\n", s)); + } + start = end; /* Skip a bit which cannot be wanted */ + /* anymore. */ + start++; + } +} + +/**************************************************************************** +do the actual mangling to 8.3 format +****************************************************************************/ +void mangle_name_83(char *s) +{ + int csum = str_checksum(s); + char *p; + char extension[4]; + char base[9]; + int baselen = 0; + int extlen = 0; + + extension[0]=0; + base[0]=0; + + p = strrchr(s,'.'); + if (p && (strlen(p+1)<4) ) + { + BOOL all_normal = (strisnormal(p+1)); /* XXXXXXXXX */ + if (all_normal && p[1] != 0) + { + *p = 0; + csum = str_checksum(s); + *p = '.'; + } + } + + + strupper(s); + + DEBUG(5,("Mangling name %s to ",s)); + + if (p) + { + if (p == s) + strcpy(extension,"___"); + else + { + *p++ = 0; + while (*p && extlen < 3) + { + if (isdoschar(*p) && *p != '.') + extension[extlen++] = *p; + p++; + } + extension[extlen] = 0; + } + } + + p = s; + + while (*p && baselen < 5) + { + if (isdoschar(*p) && *p != '.') + base[baselen++] = *p; + p++; + } + base[baselen] = 0; + + csum = csum % (36*36); + + sprintf(s,"%s%c%c%c",base,magic_char,base36(csum/36),base36(csum%36)); + + if (*extension) + { + strcat(s,"."); + strcat(s,extension); + } + DEBUG(5,("%s\n",s)); +} + + + +/******************************************************************* + work out if a name is illegal, even for long names + ******************************************************************/ +static BOOL illegal_name(char *name) +{ + static unsigned char illegal[256]; + static BOOL initialised=False; + unsigned char *s; + + if (!initialised) { + char *ill = "*\\/?<>|\":{}"; + initialised = True; + + bzero((char *)illegal,256); + for (s = (unsigned char *)ill; *s; s++) + illegal[*s] = True; + } + +#ifdef KANJI + for (s = (unsigned char *)name; *s;) { + if (is_shift_jis (*s)) { + s += 2; + } else if (illegal[*s]) { + return(True); + } else { + s++; + } + } +#else + for (s = (unsigned char *)name;*s;s++) + if (illegal[*s]) return(True); +#endif + + + return(False); +} + + +/**************************************************************************** +convert a filename to DOS format. return True if successful. +****************************************************************************/ +BOOL name_map_mangle(char *OutName,BOOL need83,int snum) +{ +#ifdef MANGLE_LONG_FILENAMES + if (!need83 && illegal_name(OutName)) need83 = True; +#endif + + /* apply any name mappings */ + { + char *map = lp_mangled_map(snum); + if (map && *map) + do_fwd_mangled_map(OutName,map); + } + + /* check if it's already in 8.3 format */ + if (need83 && !is_8_3(OutName)) { + if (!lp_manglednames(snum)) return(False); + + /* mangle it into 8.3 */ + push_mangled_name(OutName); + mangle_name_83(OutName); + } + + return(True); +} + diff --git a/source3/smbd/message.c b/source3/smbd/message.c new file mode 100644 index 0000000000..6a96b4c7a9 --- /dev/null +++ b/source3/smbd/message.c @@ -0,0 +1,204 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB messaging + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ +/* + This file handles the messaging system calls for winpopup style + messages +*/ + + +#include "includes.h" +#include "loadparm.h" + +/* look in server.c for some explanation of these variables */ +extern int DEBUGLEVEL; + + +static char msgbuf[1600]; +static int msgpos=0; +static fstring msgfrom=""; +static fstring msgto=""; + +/**************************************************************************** +deliver the message +****************************************************************************/ +static void msg_deliver(void) +{ + pstring s; + fstring name; + FILE *f; + int i; + + if (! (*lp_msg_command())) + { + DEBUG(1,("no messaging command specified\n")); + msgpos = 0; + return; + } + + /* put it in a temporary file */ + sprintf(s,"/tmp/msg.XXXXXX"); + strcpy(name,(char *)mktemp(s)); + + f = fopen(name,"w"); + if (!f) + { + DEBUG(1,("can't open message file %s\n",name)); + return; + } + + for (i=0;i<msgpos;) + { + if (msgbuf[i]=='\r' && i<(msgpos-1) && msgbuf[i+1]=='\n') + i++; + fputc(msgbuf[i++],f); + } + + fclose(f); + + + /* run the command */ + if (*lp_msg_command()) + { + strcpy(s,lp_msg_command()); + string_sub(s,"%s",name); + string_sub(s,"%f",msgfrom); + string_sub(s,"%t",msgto); + standard_sub(-1,s); + smbrun(s,NULL); + } + + msgpos = 0; +} + + + +/**************************************************************************** + reply to a sends +****************************************************************************/ +int reply_sends(char *inbuf,char *outbuf) +{ + int len; + char *orig,*dest,*msg; + int outsize = 0; + + msgpos = 0; + + + if (! (*lp_msg_command())) + return(ERROR(ERRSRV,ERRmsgoff)); + + outsize = set_message(outbuf,0,0,True); + + orig = smb_buf(inbuf)+1; + dest = skip_string(orig,1)+1; + msg = skip_string(dest,1)+1; + + strcpy(msgfrom,orig); + strcpy(msgto,dest); + + len = SVAL(msg,0); + len = MIN(len,1600-msgpos); + + memcpy(&msgbuf[msgpos],msg+2,len); + msgpos += len; + + DEBUG(3,("%s SMBsends (from %s to %s)\n",timestring(),orig,dest)); + + msg_deliver(); + + return(outsize); +} + + +/**************************************************************************** + reply to a sendstrt +****************************************************************************/ +int reply_sendstrt(char *inbuf,char *outbuf) +{ + char *orig,*dest; + int outsize = 0; + + if (! (*lp_msg_command())) + return(ERROR(ERRSRV,ERRmsgoff)); + + outsize = set_message(outbuf,1,0,True); + + msgpos = 0; + + orig = smb_buf(inbuf)+1; + dest = skip_string(orig,1)+1; + + strcpy(msgfrom,orig); + strcpy(msgto,dest); + + DEBUG(3,("%s SMBsendstrt (from %s to %s)\n",timestring(),orig,dest)); + + return(outsize); +} + + +/**************************************************************************** + reply to a sendtxt +****************************************************************************/ +int reply_sendtxt(char *inbuf,char *outbuf) +{ + int len; + int outsize = 0; + char *msg; + + if (! (*lp_msg_command())) + return(ERROR(ERRSRV,ERRmsgoff)); + + outsize = set_message(outbuf,0,0,True); + + msg = smb_buf(inbuf) + 1; + + len = SVAL(msg,0); + len = MIN(len,1600-msgpos); + + memcpy(&msgbuf[msgpos],msg+2,len); + msgpos += len; + + DEBUG(3,("%s SMBsendtxt\n",timestring())); + + return(outsize); +} + + +/**************************************************************************** + reply to a sendend +****************************************************************************/ +int reply_sendend(char *inbuf,char *outbuf) +{ + int outsize = 0; + + if (! (*lp_msg_command())) + return(ERROR(ERRSRV,ERRmsgoff)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s SMBsendend\n",timestring())); + + msg_deliver(); + + return(outsize); +} + diff --git a/source3/smbd/password.c b/source3/smbd/password.c new file mode 100644 index 0000000000..87c1fef94c --- /dev/null +++ b/source3/smbd/password.c @@ -0,0 +1,1416 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Password and authentication handling + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" + +extern int DEBUGLEVEL; +extern int Protocol; + +/* users from session setup */ +static pstring session_users=""; + +/* these are kept here to keep the string_combinations function simple */ +static char this_user[100]=""; +static char this_salt[100]=""; +static char this_crypted[100]=""; + +#ifdef SMB_PASSWD +/* Data to do lanman1/2 password challenge. */ +static unsigned char saved_challenge[8]; +static BOOL challenge_sent=False; + +/******************************************************************* +Get the next challenge value - no repeats. +********************************************************************/ +void generate_next_challenge(char *challenge) +{ + extern void E1(char *,char *,char *); + static int counter = 0; + struct timeval tval; + int v1,v2; + GetTimeOfDay(&tval); + v1 = (counter++) + getpid() + tval.tv_sec; + v2 = (counter++) * getpid() + tval.tv_usec; + SIVAL(challenge,0,v1); + SIVAL(challenge,4,v2); + E1(challenge,"SAMBA",saved_challenge); + memcpy(challenge,saved_challenge,8); + challenge_sent = True; +} + +/******************************************************************* +set the last challenge sent, usually from a password server +********************************************************************/ +BOOL set_challenge(char *challenge) +{ + memcpy(saved_challenge,challenge,8); + challenge_sent = True; + return(True); +} + +/******************************************************************* +get the last challenge sent +********************************************************************/ +BOOL last_challenge(char *challenge) +{ + if (!challenge_sent) return(False); + memcpy(challenge,saved_challenge,8); + return(True); +} +#endif + +/* this holds info on user ids that are already validated for this VC */ +static user_struct *validated_users = NULL; +static int num_validated_users = 0; + +/**************************************************************************** +check if a uid has been validated, and return an index if it has. -1 if not +****************************************************************************/ +int valid_uid(int uid) +{ + int i; + if (uid == -1) return(-1); + + for (i=0;i<num_validated_users;i++) + if (validated_users[i].uid == uid) + { + DEBUG(3,("valid uid %d mapped to vuid %d (user=%s)\n", + uid,i,validated_users[i].name)); + return(i); + } + return(-1); +} + +/**************************************************************************** +check if a uid has been validated, and return an pointer to the user_struct +if it has. NULL if not +****************************************************************************/ +user_struct *get_valid_user_struct(int uid) +{ + int vuid = valid_uid(uid); + if(vuid == -1 || validated_users[vuid].guest) + return NULL; + return &validated_users[vuid]; +} + +/**************************************************************************** +invalidate a uid +****************************************************************************/ +void invalidate_uid(int uid) +{ + int i; + for (i=0;i<num_validated_users;i++) + if (validated_users[i].uid == uid) + { + user_struct *vuser = &validated_users[i]; + vuser->uid = -1; + vuser->gid = -1; + vuser->user_ngroups = 0; + if(vuser->user_groups && + (vuser->user_groups != (gid_t *)vuser->user_igroups)) + free(vuser->user_groups); + vuser->user_groups = NULL; + if(vuser->user_igroups) + free(vuser->user_igroups); + vuser->user_igroups = NULL; + } +} + + +/**************************************************************************** +return a validated username +****************************************************************************/ +char *validated_username(int vuid) +{ + return(validated_users[vuid].name); +} + +/**************************************************************************** +register a uid/name pair as being valid and that a valid password +has been given. +****************************************************************************/ +void register_uid(int uid,int gid, char *name,BOOL guest) +{ + user_struct *vuser; + + if (valid_uid(uid) >= 0) + return; + validated_users = (user_struct *)Realloc(validated_users, + sizeof(user_struct)* + (num_validated_users+1)); + + if (!validated_users) + { + DEBUG(0,("Failed to realloc users struct!\n")); + return; + } + + vuser = &validated_users[num_validated_users]; + vuser->uid = uid; + vuser->gid = gid; + vuser->guest = guest; + strcpy(vuser->name,name); + + vuser->user_ngroups = 0; + vuser->user_groups = NULL; + vuser->user_igroups = NULL; + + /* Find all the groups this uid is in and store them. + Used by become_user() */ + setup_groups(name,uid,gid, + &vuser->user_ngroups, + &vuser->user_igroups, + &vuser->user_groups); + + DEBUG(3,("uid %d registered to name %s\n",uid,name)); + + num_validated_users++; +} + + +/**************************************************************************** +add a name to the session users list +****************************************************************************/ +void add_session_user(char *user) +{ + fstring suser; + StrnCpy(suser,user,sizeof(suser)-1); + + if (!Get_Pwnam(suser,True)) return; + + if (suser && *suser && !in_list(suser,session_users,False)) + { + if (strlen(suser) + strlen(session_users) + 2 >= sizeof(pstring)) + DEBUG(1,("Too many session users??\n")); + else + { + strcat(session_users," "); + strcat(session_users,suser); + } + } +} + + +#ifdef NO_GETSPNAM +/* a fake shadow password routine which just fills a fake spwd struct + * with the sp_pwdp field. (sreiz@aie.nl) + */ +static struct spwd *getspnam(char *username) /* fake shadow password routine */ +{ + FILE *f; + char line[1024]; + static char pw[20]; + static struct spwd static_spwd; + + static_spwd.sp_pwdp=0; + if (!(f=fopen("/etc/master.passwd", "r"))) + return 0; + while (fgets(line, 1024, f)) { + if (!strncmp(line, username, strlen(username)) && + line[strlen(username)]==':') { /* found entry */ + char *p, *q; + + p=line+strlen(username)+1; + if ((q=strchr(p, ':'))) { + *q=0; + if (q-p+1>20) + break; + strcpy(pw, p); + static_spwd.sp_pwdp=pw; + } + break; + } + } + fclose(f); + if (static_spwd.sp_pwdp) + return &static_spwd; + return 0; +} +#endif + + +#ifdef OSF1_ENH_SEC +/**************************************************************************** +an enhanced crypt for OSF1 +****************************************************************************/ +static char *osf1_bigcrypt(char *password,char *salt1) +{ + static char result[AUTH_MAX_PASSWD_LENGTH] = ""; + char *p1; + char *p2=password; + char salt[3]; + int i; + int parts = strlen(password) / AUTH_CLEARTEXT_SEG_CHARS; + if (strlen(password)%AUTH_CLEARTEXT_SEG_CHARS) + parts++; + + StrnCpy(salt,salt1,2); + StrnCpy(result,salt1,2); + + for (i=0; i<parts;i++) + { + p1 = crypt(p2,salt); + strcat(result,p1+2); + StrnCpy(salt,&result[2+i*AUTH_CIPHERTEXT_SEG_CHARS],2); + p2 += AUTH_CLEARTEXT_SEG_CHARS; + } + + return(result); +} +#endif + + +/**************************************************************************** +update the enhanced security database. Only relevant for OSF1 at the moment. +****************************************************************************/ +static void update_protected_database( char *user, BOOL result) +{ +#ifdef OSF1_ENH_SEC + struct pr_passwd *mypasswd; + time_t starttime; + long tz; + + mypasswd = getprpwnam (user); + starttime = time (NULL); + tz = mktime ( localtime ( &starttime ) ); + + if (result) + { + mypasswd->ufld.fd_slogin = tz; + mypasswd->ufld.fd_nlogins = 0; + + putprpwnam(user,mypasswd); + + DEBUG(3,("Update protected database for Account %s after succesful connection\n",user)); + } + else + { + mypasswd->ufld.fd_ulogin = tz; + mypasswd->ufld.fd_nlogins = mypasswd->ufld.fd_nlogins + 1; + if ( mypasswd->ufld.fd_max_tries != 0 && mypasswd->ufld.fd_nlogins > mypasswd->ufld.fd_max_tries ) + { + mypasswd->uflg.fg_lock = 0; + DEBUG(3,("Account is disabled -- see Account Administrator.\n")); + } + putprpwnam ( user , mypasswd ); + DEBUG(3,("Update protected database for Account %s after refusing connection\n",user)); + } +#else + DEBUG(6,("Updated database with %s %s\n",user,BOOLSTR(result))); +#endif +} + + +#ifdef AFS_AUTH +/******************************************************************* +check on AFS authentication +********************************************************************/ +static BOOL afs_auth(char *this_user,char *password) +{ + long password_expires = 0; + char *reason; + + /* For versions of AFS prior to 3.3, this routine has few arguments, */ + /* but since I can't find the old documentation... :-) */ + setpag(); + if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION+KA_USERAUTH_DOSETPAG, + this_user, + (char *) 0, /* instance */ + (char *) 0, /* cell */ + password, + 0, /* lifetime, default */ + &password_expires, /*days 'til it expires */ + 0, /* spare 2 */ + &reason) == 0) + return(True); + return(False); +} +#endif + + +#ifdef DFS_AUTH + +sec_login_handle_t my_dce_sec_context; +int dcelogin_atmost_once = 0; + +/******************************************************************* +check on a DCE/DFS authentication +********************************************************************/ +static BOOL dfs_auth(char *this_user,char *password) +{ + error_status_t err; + int err2; + int prterr; + boolean32 password_reset; + sec_passwd_rec_t my_dce_password; + sec_login_auth_src_t auth_src = sec_login_auth_src_network; + unsigned char dce_errstr[dce_c_error_string_len]; + + /* + * We only go for a DCE login context if the given password + * matches that stored in the local password file.. + * Assumes local passwd file is kept in sync w/ DCE RGY! + */ + + if (!strcmp((char *)crypt(password,this_salt),this_crypted) || + dcelogin_atmost_once) + return(False); + + if (sec_login_setup_identity( + (unsigned char *)this_user, + sec_login_no_flags, + &my_dce_sec_context, + &err) == 0) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE Setup Identity for %s failed: %s\n", + this_user,dce_errstr)); + return(False); + } + + my_dce_password.version_number = sec_passwd_c_version_none; + my_dce_password.pepper = NULL; + my_dce_password.key.key_type = sec_passwd_plain; + my_dce_password.key.tagged_union.plain = (idl_char *)password; + + if (sec_login_valid_and_cert_ident(my_dce_sec_context, + &my_dce_password, + &password_reset, + &auth_src, + &err) == 0 ) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE Identity Validation failed for principal %s: %s\n", + this_user,dce_errstr)); + + return(False); + } + + sec_login_set_context(my_dce_sec_context, &err); + if (err != error_status_ok ) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE login failed for principal %s, cant set context: %s\n", + this_user,dce_errstr)); + sec_login_purge_context(my_dce_sec_context, &err); + return(False); + } + else + { + DEBUG(0,("DCE login succeeded for principal %s on pid %d\n", + this_user, getpid())); + } + + dcelogin_atmost_once = 1; + return (True); +} + +void dfs_unlogin(void) +{ + error_status_t err; + int err2; + unsigned char dce_errstr[dce_c_error_string_len]; + + sec_login_purge_context(my_dce_sec_context, &err); + if (err != error_status_ok ) + { + dce_error_inq_text(err, dce_errstr, &err2); + DEBUG(0,("DCE purge login context failed for server instance %d: %s\n", + getpid(), dce_errstr)); + } +} + +#endif + + +#ifdef LINUX_BIGCRYPT +/**************************************************************************** +an enhanced crypt for Linux to handle password longer than 8 characters +****************************************************************************/ +static int linux_bigcrypt(char *password,char *salt1, char *crypted) +{ +#define LINUX_PASSWORD_SEG_CHARS 8 + char salt[3]; + int i; + + StrnCpy(salt,salt1,2); + crypted +=2; + + for ( i=strlen(password); i > 0; i -= LINUX_PASSWORD_SEG_CHARS) { + char * p = crypt(password,salt) + 2; + if(strncmp(p, crypted, LINUX_PASSWORD_SEG_CHARS) != 0) + return(0); + password += LINUX_PASSWORD_SEG_CHARS; + crypted += strlen(p); + } + + return(1); +} +#endif + + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static BOOL string_combinations2(char *s,int offset,BOOL (*fn)(),int N) +{ + int len = strlen(s); + int i; + +#ifdef PASSWORD_LENGTH + len = MIN(len,PASSWORD_LENGTH); +#endif + + if (N <= 0 || offset >= len) + return(fn(s)); + + for (i=offset;i<(len-(N-1));i++) + { + char c = s[i]; + if (!islower(c)) continue; + s[i] = toupper(c); + if (string_combinations2(s,i+1,fn,N-1)) + return(True); + s[i] = c; + } + return(False); +} + +/**************************************************************************** +apply a function to upper/lower case combinations +of a string and return true if one of them returns true. +try all combinations with up to N uppercase letters. +offset is the first char to try and change (start with 0) +it assumes the string starts lowercased +****************************************************************************/ +static BOOL string_combinations(char *s,BOOL (*fn)(),int N) +{ + int n; + for (n=1;n<=N;n++) + if (string_combinations2(s,0,fn,n)) return(True); + return(False); +} + + + +/**************************************************************************** +core of password checking routine +****************************************************************************/ +BOOL password_check(char *password) +{ +#ifdef AFS_AUTH + if (afs_auth(this_user,password)) return(True); +#endif + +#ifdef DFS_AUTH + if (dfs_auth(this_user,password)) return(True); +#endif + +#ifdef PWDAUTH + if (pwdauth(this_user,password) == 0) + return(True); +#endif + +#ifdef OSF1_ENH_SEC + return(strcmp(osf1_bigcrypt(password,this_salt),this_crypted) == 0); +#endif + +#ifdef ULTRIX_AUTH + return (strcmp((char *)crypt16(password, this_salt ),this_crypted) == 0); +#endif + +#ifdef LINUX_BIGCRYPT + return(linux_bigcrypt(password,this_salt,this_crypted)); +#endif + +#ifdef NO_CRYPT + DEBUG(1,("Warning - no crypt available\n")); + return(False); +#else + return(strcmp((char *)crypt(password,this_salt),this_crypted) == 0); +#endif +} + +#ifdef SMB_PASSWD +/**************************************************************************** +core of smb password checking routine. +****************************************************************************/ +BOOL smb_password_check(char *password, unsigned char *part_passwd, unsigned char *c8) +{ + /* Finish the encryption of part_passwd. */ + unsigned char p21[21]; + unsigned char p24[24]; + + if(part_passwd == NULL) + DEBUG(10,("No password set - allowing access\n")); + /* No password set - always true ! */ + if(part_passwd == NULL) + return 1; + + memset(p21,'\0',21); + memcpy(p21,part_passwd,16); + E_P24(p21, c8, p24); +#if DEBUG_PASSWORD + { + int i; + DEBUG(100,("Part password (P16) was |")); + for(i = 0; i < 16; i++) + DEBUG(100,("%X ", (unsigned char)part_passwd[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Password from client was |")); + for(i = 0; i < 24; i++) + DEBUG(100,("%X ", (unsigned char)password[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Given challenge was |")); + for(i = 0; i < 8; i++) + DEBUG(100,("%X ", (unsigned char)c8[i])); + DEBUG(100,("|\n")); + DEBUG(100,("Value from encryption was |")); + for(i = 0; i < 24; i++) + DEBUG(100,("%X ", (unsigned char)p24[i])); + DEBUG(100,("|\n")); + } +#endif + return (memcmp(p24, password, 24) == 0); +} +#endif + +/**************************************************************************** +check if a username/password is OK +****************************************************************************/ +BOOL password_ok(char *user,char *password, int pwlen, struct passwd *pwd, BOOL is_nt_password) +{ + pstring pass2; + int level = lp_passwordlevel(); + struct passwd *pass; +#ifdef SMB_PASSWD + char challenge[8]; + struct smb_passwd *smb_pass; + BOOL challenge_done = False; +#endif + + if (password) password[pwlen] = 0; + +#ifdef SMB_PASSWD + if (pwlen == 24) + challenge_done = last_challenge(challenge); +#endif + +#if DEBUG_PASSWORD +#ifdef SMB_PASSWD + if (challenge_done) + { + int i; + DEBUG(100,("checking user=[%s] pass=[",user)); + for( i = 0; i < 24; i++) + DEBUG(100,("%0x ", (unsigned char)password[i])); + DEBUG(100,("]\n")); + } + else +#endif + DEBUG(100,("checking user=[%s] pass=[%s]\n",user,password)); +#endif + + if (!password) + return(False); + + if (((!*password) || (!pwlen)) && !lp_null_passwords()) + return(False); + + if (pwd && !user) + { + pass = (struct passwd *) pwd; + user = pass->pw_name; + } + else + pass = Get_Pwnam(user,True); + +#ifdef SMB_PASSWD + + DEBUG(4,("SMB Password - pwlen = %d, challenge_done = %d\n", pwlen, challenge_done)); + + if((pwlen == 24) && challenge_done) + { + DEBUG(4,("Checking SMB password for user %s (l=24)\n",user)); + + if (!pass) + { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); + } + + smb_pass = get_smbpwnam(user); + if(!smb_pass) + { + DEBUG(3,("Couldn't find user %s in smb_passwd file.\n", user)); + return(False); + } + + /* Ensure the uid's match */ + if(smb_pass->smb_userid != pass->pw_uid) + { + DEBUG(3,("Error : UNIX and SMB uids in password files do not match !\n")); + return(False); + } + + if(Protocol >= PROTOCOL_NT1 && is_nt_password) + { + /* We have the NT MD4 hash challenge available - see if we can + use it (ie. does it exist in the smbpasswd file). + */ + if(smb_pass->smb_nt_passwd != NULL) + { + DEBUG(4,("Checking NT MD4 password\n")); + if(smb_password_check(password, smb_pass->smb_nt_passwd, challenge)) + { + update_protected_database(user,True); + return(True); + } + DEBUG(4,("NT MD4 password check failed\n")); + return (False); + } + } + + /* Try against the lanman password */ + + if(smb_password_check(password, smb_pass->smb_passwd, challenge)) + { + update_protected_database(user,True); + return(True); + } + + DEBUG(3,("Error smb_password_check failed\n")); + } +#endif + + DEBUG(4,("Checking password for user %s (l=%d)\n",user,pwlen)); + + if (!pass) + { + DEBUG(3,("Couldn't find user %s\n",user)); + return(False); + } + +#ifdef SHADOW_PWD + { + struct spwd *spass; + + /* many shadow systems require you to be root to get the password, + in most cases this should already be the case when this + function is called, except perhaps for IPC password changing + requests */ + + spass = getspnam(pass->pw_name); + if (spass && spass->sp_pwdp) + pass->pw_passwd = spass->sp_pwdp; + } +#endif + +#ifdef SecureWare + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pass->pw_passwd = pr_pw->ufld.fd_encrypt; + } +#endif + +#ifdef HPUX_10_TRUSTED + { + struct pr_passwd *pr_pw = getprpwnam(pass->pw_name); + if (pr_pw && pr_pw->ufld.fd_encrypt) + pass->pw_passwd = pr_pw->ufld.fd_encrypt; + } +#endif + +#ifdef OSF1_ENH_SEC + { + struct pr_passwd *mypasswd; + DEBUG(5,("Checking password for user %s in OSF1_ENH_SEC\n",user)); + mypasswd = getprpwnam (user); + if ( mypasswd ) + { + strcpy(pass->pw_name,mypasswd->ufld.fd_name); + strcpy(pass->pw_passwd,mypasswd->ufld.fd_encrypt); + } + else + { + DEBUG(5,("No entry for user %s in protected database !\n",user)); + return(False); + } + } +#endif + +#ifdef ULTRIX_AUTH + { + AUTHORIZATION *ap = getauthuid( pass->pw_uid ); + if (ap) + { + strcpy( pass->pw_passwd, ap->a_password ); + endauthent(); + } + } +#endif + + /* extract relevant info */ + strcpy(this_user,pass->pw_name); + strcpy(this_salt,pass->pw_passwd); + strcpy(this_crypted,pass->pw_passwd); + + if (!*this_crypted) { + if (!lp_null_passwords()) { + DEBUG(2,("Disallowing access to %s due to null password\n",this_user)); + return(False); + } +#ifndef PWDAUTH + if (!*password) { + DEBUG(3,("Allowing access to %s with null password\n",this_user)); + return(True); + } +#endif + } + + /* try it as it came to us */ + if (password_check(password)) + { + update_protected_database(user,True); + return(True); + } + + /* if the password was given to us with mixed case then we don't + need to proceed as we know it hasn't been case modified by the + client */ + if (strhasupper(password) && strhaslower(password)) + return(False); + + /* make a copy of it */ + StrnCpy(pass2,password,sizeof(pstring)-1); + + /* try all lowercase */ + strlower(password); + if (password_check(password)) + { + update_protected_database(user,True); + return(True); + } + + /* give up? */ + if(level < 1) + { + update_protected_database(user,False); + + /* restore it */ + strcpy(password,pass2); + + return(False); + } + + /* last chance - all combinations of up to level chars upper! */ + strlower(password); + + if (string_combinations(password,password_check,level)) + { + update_protected_database(user,True); + return(True); + } + + update_protected_database(user,False); + + /* restore it */ + strcpy(password,pass2); + + return(False); +} + + + +/**************************************************************************** +check if a username is valid +****************************************************************************/ +BOOL user_ok(char *user,int snum) +{ + pstring valid, invalid; + BOOL ret; + + StrnCpy(valid, lp_valid_users(snum), sizeof(pstring)); + StrnCpy(invalid, lp_invalid_users(snum), sizeof(pstring)); + + string_sub(valid,"%S",lp_servicename(snum)); + string_sub(invalid,"%S",lp_servicename(snum)); + + ret = !user_in_list(user,invalid); + + if (ret && valid && *valid) + ret = user_in_list(user,valid); + + if (ret && lp_onlyuser(snum)) { + char *user_list = lp_username(snum); + string_sub(user_list,"%S",lp_servicename(snum)); + ret = user_in_list(user,user_list); + } + + return(ret); +} + + + + +/**************************************************************************** +validate a group username entry. Return the username or NULL +****************************************************************************/ +static char *validate_group(char *group,char *password,int pwlen,int snum) +{ +#ifdef NETGROUP + { + char *host, *user, *domain; + setnetgrent(group); + while (getnetgrent(&host, &user, &domain)) { + if (user) { + if (user_ok(user, snum) && + password_ok(user,password,pwlen,NULL,False)) { + endnetgrent(); + return(user); + } + } + } + endnetgrent(); + } +#endif + +#if HAVE_GETGRNAM + { + struct group *gptr = (struct group *)getgrnam(group); + char **member; + if (gptr) + { + member = gptr->gr_mem; + while (member && *member) + { + static fstring name; + strcpy(name,*member); + if (user_ok(name,snum) && + password_ok(name,password,pwlen,NULL,False)) + return(&name[0]); + member++; + } +#ifdef GROUP_CHECK_PWENT + { + struct passwd *pwd; + static fstring tm; + + setpwent (); + while (pwd = getpwent ()) { + if (*(pwd->pw_passwd) && pwd->pw_gid == gptr->gr_gid) { + /* This Entry have PASSWORD and same GID then check pwd */ + if (password_ok(NULL, password, pwlen, pwd,False)) { + strcpy(tm, pwd->pw_name); + endpwent (); + return tm; + } + } + } + endpwent (); + } +#endif /* GROUP_CHECK_PWENT */ + } + } +#endif + return(NULL); +} + + + +/**************************************************************************** +check for authority to login to a service with a given username/password +****************************************************************************/ +BOOL authorise_login(int snum,char *user,char *password, int pwlen, + BOOL *guest,BOOL *force,int vuid) +{ + BOOL ok = False; + + *guest = False; + +#if DEBUG_PASSWORD + DEBUG(100,("checking authorisation on user=%s pass=%s\n",user,password)); +#endif + + /* there are several possabilities: + 1) login as the given user with given password + 2) login as a previously registered username with the given password + 3) login as a session list username with the given password + 4) login as a previously validated user/password pair + 5) login as the "user =" user with given password + 6) login as the "user =" user with no password (guest connection) + 7) login as guest user with no password + + if the service is guest_only then steps 1 to 5 are skipped + */ + + if (GUEST_ONLY(snum)) *force = True; + + if (!(GUEST_ONLY(snum) && GUEST_OK(snum))) + { + + /* check the given username and password */ + if (!ok && (*user) && user_ok(user,snum)) { + ok = password_ok(user,password, pwlen, NULL, False); + if (ok) DEBUG(3,("ACCEPTED: given username password ok\n")); + } + + /* check for a previously registered guest username */ + if (!ok && (vuid >= 0) && validated_users[vuid].guest) { + if (user_ok(validated_users[vuid].name,snum) && + password_ok(validated_users[vuid].name, password, pwlen, NULL, False)) { + strcpy(user, validated_users[vuid].name); + validated_users[vuid].guest = False; + DEBUG(3,("ACCEPTED: given password with registered user %s\n", user)); + ok = True; + } + } + + + /* now check the list of session users */ + if (!ok) + { + char *auser; + char *user_list = strdup(session_users); + if (!user_list) return(False); + + for (auser=strtok(user_list,LIST_SEP); + !ok && auser; + auser = strtok(NULL,LIST_SEP)) + { + fstring user2; + strcpy(user2,auser); + if (!user_ok(user2,snum)) continue; + + if (password_ok(user2,password, pwlen, NULL, False)) { + ok = True; + strcpy(user,user2); + DEBUG(3,("ACCEPTED: session list username and given password ok\n")); + } + } + free(user_list); + } + + /* check for a previously validated username/password pair */ + if (!ok && !lp_revalidate(snum) && + (vuid >= 0) && !validated_users[vuid].guest && + user_ok(validated_users[vuid].name,snum)) { + strcpy(user,validated_users[vuid].name); + *guest = False; + DEBUG(3,("ACCEPTED: validated uid ok as non-guest\n")); + ok = True; + } + + /* check for a rhosts entry */ + if (!ok && user_ok(user,snum) && check_hosts_equiv(user)) { + ok = True; + DEBUG(3,("ACCEPTED: hosts equiv or rhosts entry\n")); + } + + /* check the user= fields and the given password */ + if (!ok && lp_username(snum)) { + char *auser; + pstring user_list; + StrnCpy(user_list,lp_username(snum),sizeof(pstring)); + + string_sub(user_list,"%S",lp_servicename(snum)); + + for (auser=strtok(user_list,LIST_SEP); + auser && !ok; + auser = strtok(NULL,LIST_SEP)) + { + if (*auser == '@') + { + auser = validate_group(auser+1,password,pwlen,snum); + if (auser) + { + ok = True; + strcpy(user,auser); + DEBUG(3,("ACCEPTED: group username and given password ok\n")); + } + } + else + { + fstring user2; + strcpy(user2,auser); + if (user_ok(user2,snum) && + password_ok(user2,password,pwlen,NULL, False)) + { + ok = True; + strcpy(user,user2); + DEBUG(3,("ACCEPTED: user list username and given password ok\n")); + } + } + } + } + } /* not guest only */ + + /* check for a normal guest connection */ + if (!ok && GUEST_OK(snum)) + { + fstring guestname; + StrnCpy(guestname,lp_guestaccount(snum),sizeof(guestname)-1); + if (Get_Pwnam(guestname,True)) + { + strcpy(user,guestname); + ok = True; + DEBUG(3,("ACCEPTED: guest account and guest ok\n")); + } + else + DEBUG(0,("Invalid guest account %s??\n",guestname)); + *guest = True; + *force = True; + } + + if (ok && !user_ok(user,snum)) + { + DEBUG(0,("rejected invalid user %s\n",user)); + ok = False; + } + + return(ok); +} + + +/**************************************************************************** +read the a hosts.equiv or .rhosts file and check if it +allows this user from this machine +****************************************************************************/ +static BOOL check_user_equiv(char *user, char *remote, char *equiv_file) +{ + pstring buf; + int plus_allowed = 1; + char *file_host; + char *file_user; + FILE *fp = fopen(equiv_file, "r"); + DEBUG(5, ("check_user_equiv %s %s %s\n", user, remote, equiv_file)); + if (! fp) return False; + while(fgets(buf, sizeof(buf), fp)) + { + trim_string(buf," "," "); + + if (buf[0] != '#' && buf[0] != '\n') + { + BOOL is_group = False; + int plus = 1; + char *bp = buf; + if (strcmp(buf, "NO_PLUS\n") == 0) + { + DEBUG(6, ("check_user_equiv NO_PLUS\n")); + plus_allowed = 0; + } + else { + if (buf[0] == '+') + { + bp++; + if (*bp == '\n' && plus_allowed) + { + /* a bare plus means everbody allowed */ + DEBUG(6, ("check_user_equiv everybody allowed\n")); + fclose(fp); + return True; + } + } + else if (buf[0] == '-') + { + bp++; + plus = 0; + } + if (*bp == '@') + { + is_group = True; + bp++; + } + file_host = strtok(bp, " \t\n"); + file_user = strtok(NULL, " \t\n"); + DEBUG(7, ("check_user_equiv %s %s\n", file_host, file_user)); + if (file_host && *file_host) + { + BOOL host_ok = False; + +#ifdef NETGROUP + /* THIS IS UNTESTED!! */ + if (is_group) + { + static char *mydomain = NULL; + if (!mydomain) + yp_get_default_domain(&mydomain); + if (mydomain && innetgr(remote,file_host,user,mydomain)) + host_ok = True; + } +#else + if (is_group) + { + DEBUG(1,("Netgroups not configured - add -DNETGROUP and recompile\n")); + continue; + } +#endif + + /* is it this host */ + /* the fact that remote has come from a call of gethostbyaddr + * means that it may have the fully qualified domain name + * so we could look up the file version to get it into + * a canonical form, but I would rather just type it + * in full in the equiv file + */ + if (!host_ok && !is_group && strequal(remote, file_host)) + host_ok = True; + + if (!host_ok) + continue; + + /* is it this user */ + if (file_user == 0 || strequal(user, file_user)) + { + fclose(fp); + DEBUG(5, ("check_user_equiv matched %s%s %s\n", + (plus ? "+" : "-"), file_host, + (file_user ? file_user : ""))); + return (plus ? True : False); + } + } + } + } + } + fclose(fp); + return False; +} + + +/**************************************************************************** +check for a possible hosts equiv or rhosts entry for the user +****************************************************************************/ +BOOL check_hosts_equiv(char *user) +{ + char *fname = NULL; + pstring rhostsfile; + struct passwd *pass = Get_Pwnam(user,True); + + extern struct from_host Client_info; + extern int Client; + + if (!pass) + return(False); + + fromhost(Client,&Client_info); + + fname = lp_hosts_equiv(); + + /* note: don't allow hosts.equiv on root */ + if (fname && *fname && (pass->pw_uid != 0)) + { + if (check_user_equiv(user,Client_info.name,fname)) + return(True); + } + + if (lp_use_rhosts()) + { + char *home = get_home_dir(user); + if (home) + { + sprintf(rhostsfile, "%s/.rhosts", home); + if (check_user_equiv(user,Client_info.name,rhostsfile)) + return(True); + } + } + + return(False); +} + + +static int password_client = -1; +static fstring pserver; + +/**************************************************************************** +attempted support for server level security +****************************************************************************/ +BOOL server_cryptkey(char *buf) +{ + pstring inbuf,outbuf; + fstring pass_protocol; + extern fstring remote_machine; + char *p; + int len; + fstring desthost; + struct in_addr dest_ip; + extern struct in_addr myip; + int port = 139; + BOOL ret; + + if (password_client >= 0) + close(password_client); + password_client = -1; + + if (Protocol < PROTOCOL_NT1) { + strcpy(pass_protocol,"LM1.2X002"); + } else { + strcpy(pass_protocol,"NT LM 0.12"); + } + + bzero(inbuf,sizeof(inbuf)); + bzero(outbuf,sizeof(outbuf)); + + for (p=strtok(lp_passwordserver(),LIST_SEP); p ; p = strtok(NULL,LIST_SEP)) { + strcpy(desthost,p); + standard_sub_basic(desthost); + strupper(desthost); + + dest_ip = *interpret_addr2(desthost); + if (zero_ip(dest_ip)) { + DEBUG(1,("Can't resolve address for %s\n",p)); + continue; + } + + if (memcmp(&dest_ip,&myip,sizeof(dest_ip)) == 0) { + DEBUG(1,("Password server loop - disabling password server %s\n",p)); + continue; + } + + password_client = open_socket_out(SOCK_STREAM, &dest_ip, port); + if (password_client >= 0) { + DEBUG(3,("connected to password server %s\n",p)); + StrnCpy(pserver,p,sizeof(pserver)-1); + break; + } + } + + if (password_client < 0) { + DEBUG(1,("password server not available\n")); + return(False); + } + + + /* send a session request (RFC 8002) */ + + /* put in the destination name */ + len = 4; + p = outbuf+len; + name_mangle(desthost,p,' '); + len += name_len(p); + + /* and my name */ + p = outbuf+len; + name_mangle(remote_machine,p,' '); + len += name_len(p); + + _smb_setlen(outbuf,len); + CVAL(outbuf,0) = 0x81; + + send_smb(password_client,outbuf); + receive_smb(password_client,inbuf,5000); + + if (CVAL(inbuf,0) != 0x82) { + DEBUG(1,("%s rejected the session\n",pserver)); + close(password_client); password_client = -1; + return(False); + } + + DEBUG(3,("got session\n")); + + bzero(outbuf,smb_size); + + /* setup the protocol string */ + set_message(outbuf,0,strlen(pass_protocol)+2,True); + p = smb_buf(outbuf); + *p++ = 2; + strcpy(p,pass_protocol); + + CVAL(outbuf,smb_com) = SMBnegprot; + CVAL(outbuf,smb_flg) = 0x8; + SSVAL(outbuf,smb_flg2,0x1); + + send_smb(password_client,outbuf); + ret = receive_smb(password_client,inbuf,5000); + + if (!ret || CVAL(inbuf,smb_rcls) || SVAL(inbuf,smb_vwv0)) { + DEBUG(1,("%s rejected the protocol\n",pserver)); + close(password_client); password_client= -1; + return(False); + } + + if (!(CVAL(inbuf,smb_vwv1) & 1)) { + DEBUG(1,("%s isn't in user level security mode\n",pserver)); + close(password_client); password_client= -1; + return(False); + } + + memcpy(buf,inbuf,smb_len(inbuf)+4); + + DEBUG(3,("password server OK\n")); + + return(True); +} + +/**************************************************************************** +attempted support for server level security +****************************************************************************/ +BOOL server_validate(char *buf) +{ + pstring inbuf,outbuf; + BOOL ret; + + if (password_client < 0) { + DEBUG(1,("%s not connected\n",pserver)); + return(False); + } + + bzero(inbuf,sizeof(inbuf)); + memcpy(outbuf,buf,sizeof(outbuf)); + + /* send a session setup command */ + CVAL(outbuf,smb_flg) = 0x8; + SSVAL(outbuf,smb_flg2,0x1); + CVAL(outbuf,smb_vwv0) = 0xFF; + + set_message(outbuf,smb_numwords(outbuf),smb_buflen(outbuf),False); + + SCVAL(inbuf,smb_rcls,1); + + send_smb(password_client,outbuf); + ret = receive_smb(password_client,inbuf,5000); + + if (!ret || CVAL(inbuf,smb_rcls) != 0) { + DEBUG(1,("password server %s rejected the password\n",pserver)); + return(False); + } + + /* if logged in as guest then reject */ + if ((SVAL(inbuf,smb_vwv2) & 1) != 0) { + DEBUG(1,("password server %s gave us guest only\n",pserver)); + return(False); + } + + DEBUG(3,("password server %s accepted the password\n",pserver)); + +#ifndef KEEP_PASSWORD_SERVER_OPEN + close(password_client); password_client= -1; +#endif + + return(True); +} + + diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c new file mode 100644 index 0000000000..b7b51775bb --- /dev/null +++ b/source3/smbd/reply.c @@ -0,0 +1,3210 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Main SMB reply routines + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ +/* + This file handles most of the reply_ calls that the server + makes to handle specific protocols +*/ + + +#include "includes.h" +#include "loadparm.h" +#include "trans2.h" + +/* look in server.c for some explanation of these variables */ +extern int Protocol; +extern int DEBUGLEVEL; +extern int chain_size; +extern int maxxmit; +extern int chain_fnum; +extern char magic_char; +extern connection_struct Connections[]; +extern files_struct Files[]; +extern BOOL case_sensitive; +extern pstring sesssetup_user; +extern int Client; + +/* this macro should always be used to extract an fnum (smb_fid) from +a packet to ensure chaining works correctly */ +#define GETFNUM(buf,where) (chain_fnum!= -1?chain_fnum:SVAL(buf,where)) + + +/**************************************************************************** + reply to an special message +****************************************************************************/ +int reply_special(char *inbuf,char *outbuf) +{ + int outsize = 4; + int msg_type = CVAL(inbuf,0); + int msg_flags = CVAL(inbuf,1); + pstring name1,name2; + extern fstring remote_machine; + extern fstring local_machine; + char *p; + + *name1 = *name2 = 0; + + smb_setlen(outbuf,0); + + switch (msg_type) + { + case 0x81: /* session request */ + CVAL(outbuf,0) = 0x82; + CVAL(outbuf,3) = 0; + if (name_len(inbuf+4) > 50) + { + DEBUG(0,("Invalid name length in session request\n")); + return(0); + } + name_extract(inbuf,4,name1); + name_extract(inbuf,4 + name_len(inbuf + 4),name2); + DEBUG(2,("netbios connect: name1=%s name2=%s\n",name1,name2)); + + strcpy(remote_machine,name2); + trim_string(remote_machine," "," "); + p = strchr(remote_machine,' '); + strlower(remote_machine); + if (p) *p = 0; + + strcpy(local_machine,name1); + trim_string(local_machine," "," "); + p = strchr(local_machine,' '); + strlower(local_machine); + if (p) *p = 0; + + add_session_user(remote_machine); + + reload_services(True); + reopen_logs(); + + break; + case 0x85: /* session keepalive */ + default: + return(0); + } + + DEBUG(5,("%s init msg_type=0x%x msg_flags=0x%x\n",timestring(),msg_type,msg_flags)); + + return(outsize); +} + + +/******************************************************************* +work out what error to give to a failed connection +********************************************************************/ +static int connection_error(char *inbuf,char *outbuf,int connection_num) +{ + switch (connection_num) + { + case -8: + return(ERROR(ERRSRV,ERRnoresource)); + case -7: + return(ERROR(ERRSRV,ERRbaduid)); + case -6: + return(ERROR(ERRSRV,ERRinvdevice)); + case -5: + return(ERROR(ERRSRV,ERRinvnetname)); + case -4: + return(ERROR(ERRSRV,ERRaccess)); + case -3: + return(ERROR(ERRDOS,ERRnoipc)); + case -2: + return(ERROR(ERRSRV,ERRinvnetname)); + } + return(ERROR(ERRSRV,ERRbadpw)); +} + + +/**************************************************************************** + reply to a tcon +****************************************************************************/ +int reply_tcon(char *inbuf,char *outbuf) +{ + pstring service; + pstring user; + pstring password; + pstring dev; + int connection_num; + int outsize = 0; + int uid = SVAL(inbuf,smb_uid); + int vuid; + int pwlen; + + *service = *user = *password = *dev = 0; + + vuid = valid_uid(uid); + + parse_connect(inbuf,service,user,password,&pwlen,dev); + + connection_num = make_connection(service,user,password,pwlen,dev,vuid); + + if (connection_num < 0) + return(connection_error(inbuf,outbuf,connection_num)); + + outsize = set_message(outbuf,2,0,True); + SSVAL(outbuf,smb_vwv0,maxxmit); + SSVAL(outbuf,smb_vwv1,connection_num); + SSVAL(outbuf,smb_tid,connection_num); + + DEBUG(3,("%s tcon service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + + return(outsize); +} + + +/**************************************************************************** + reply to a tcon and X +****************************************************************************/ +int reply_tcon_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring service; + pstring user; + pstring password; + pstring devicename; + int connection_num; + int outsize = 0; + int uid = SVAL(inbuf,smb_uid); + int vuid; + int smb_com2 = SVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int passlen = SVAL(inbuf,smb_vwv3); + + *service = *user = *password = *devicename = 0; + + /* we might have to close an old one */ + if ((SVAL(inbuf,smb_vwv2) & 0x1) != 0) + close_cnum(SVAL(inbuf,smb_tid),uid); + + vuid = valid_uid(uid); + + { + char *path; + char *p; + memcpy(password,smb_buf(inbuf),passlen); + password[passlen]=0; + path = smb_buf(inbuf) + passlen; + DEBUG(4,("parsing net-path %s, passlen=%d\n",path,passlen)); + strcpy(service,path+2); + p = strchr(service,'\\'); + if (!p) + return(ERROR(ERRSRV,ERRinvnetname)); + *p = 0; + strcpy(service,p+1); + p = strchr(service,'%'); + if (p) + { + *p++ = 0; + strcpy(user,p); + } + StrnCpy(devicename,path + strlen(path) + 1,6); + DEBUG(4,("Got device type %s\n",devicename)); + } + + connection_num = make_connection(service,user,password,passlen,devicename,vuid); + + if (connection_num < 0) + return(connection_error(inbuf,outbuf,connection_num)); + + outsize = set_message(outbuf,2,strlen(devicename)+1,True); + + DEBUG(3,("%s tconX service=%s user=%s cnum=%d\n",timestring(),service,user,connection_num)); + + /* set the incoming and outgoing tid to the just created one */ + SSVAL(inbuf,smb_tid,connection_num); + SSVAL(outbuf,smb_tid,connection_num); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size + outsize)-4); + + strcpy(smb_buf(outbuf),devicename); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + return(outsize); +} + + +/**************************************************************************** + reply to an unknown type +****************************************************************************/ +int reply_unknown(char *inbuf,char *outbuf) +{ + int cnum; + int type; + cnum = SVAL(inbuf,smb_tid); + type = CVAL(inbuf,smb_com); + + DEBUG(0,("%s unknown command type (%s): cnum=%d type=%d (0x%X)\n", + timestring(), + smb_fn_name(type), + cnum,type,type)); + + return(ERROR(ERRSRV,ERRunknownsmb)); +} + + +/**************************************************************************** + reply to an ioctl +****************************************************************************/ +int reply_ioctl(char *inbuf,char *outbuf) +{ + DEBUG(3,("ignoring ioctl\n")); + + return(ERROR(ERRSRV,ERRnosupport)); +} + + +/**************************************************************************** +reply to a session setup command +****************************************************************************/ +int reply_sesssetup_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int sess_uid; + int gid; + int smb_com2; + int smb_off2; + int smb_bufsize; + int smb_mpxmax; + int smb_vc_num; + uint32 smb_sesskey; + int smb_apasslen; + pstring smb_apasswd; + int smb_ntpasslen = 0; + pstring smb_ntpasswd; + BOOL valid_nt_password = False; + pstring user; + BOOL guest=False; + + *smb_apasswd = 0; + + sess_uid = SVAL(inbuf,smb_uid); + smb_com2 = CVAL(inbuf,smb_vwv0); + smb_off2 = SVAL(inbuf,smb_vwv1); + smb_bufsize = SVAL(inbuf,smb_vwv2); + smb_mpxmax = SVAL(inbuf,smb_vwv3); + smb_vc_num = SVAL(inbuf,smb_vwv4); + smb_sesskey = IVAL(inbuf,smb_vwv5); + + if (Protocol < PROTOCOL_NT1) { + smb_apasslen = SVAL(inbuf,smb_vwv7); + memcpy(smb_apasswd,smb_buf(inbuf),smb_apasslen); + StrnCpy(user,smb_buf(inbuf)+smb_apasslen,sizeof(user)-1); + } else { + uint16 passlen1 = SVAL(inbuf,smb_vwv7); + uint16 passlen2 = SVAL(inbuf,smb_vwv8); + BOOL doencrypt = SMBENCRYPT(); + char *p = smb_buf(inbuf); + if (passlen1 > 256) passlen1 = 0; + if (passlen2 > 256) passlen2 = 0; /* I don't know why NT gives weird + lengths sometimes */ + if(doencrypt) { + /* Save the lanman2 password and the NT md4 password. */ + smb_apasslen = passlen1; + memcpy(smb_apasswd,p,smb_apasslen); + smb_ntpasslen = passlen2; + memcpy(smb_ntpasswd,p+passlen1,smb_ntpasslen); + } else { + /* for Win95 */ + if (passlen1 > passlen2) { + smb_apasslen = passlen1; + StrnCpy(smb_apasswd,p,smb_apasslen); + } else { + smb_apasslen = passlen2; + StrnCpy(smb_apasswd,p + passlen1,smb_apasslen); + } + } + if (passlen2 == 1) { + /* apparently NT sometimes sets passlen2 to 1 when it means 0. This + tries to work around that problem */ + passlen2 = 0; + } + p += passlen1 + passlen2; + strcpy(user,p); p = skip_string(p,1); + DEBUG(3,("Domain=[%s] NativeOS=[%s] NativeLanMan=[%s]\n", + p,skip_string(p,1),skip_string(p,2))); + } + + + DEBUG(3,("sesssetupX:name=[%s]\n",user)); + + if (!*user) + strcpy(user,lp_guestaccount(-1)); + + strlower(user); + + strcpy(sesssetup_user,user); + + reload_services(True); + + add_session_user(user); + + + if (!(lp_security() == SEC_SERVER && server_validate(inbuf)) && + !check_hosts_equiv(user)) + { + + if (strequal(user,lp_guestaccount(-1)) && (*smb_apasswd == 0)) + guest = True; + + /* now check if it's a valid username/password */ + /* If an NT password was supplied try and validate with that + first. This is superior as the passwords are mixed case 128 length unicode */ + if(smb_ntpasslen && !guest) + { + if(!password_ok(user,smb_ntpasswd,smb_ntpasslen,NULL,True)) + DEBUG(0,("NT Password did not match ! Defaulting to Lanman\n")); + else + valid_nt_password = True; + } + if (!valid_nt_password && !guest && !password_ok(user,smb_apasswd,smb_apasslen,NULL,False)) + { + if (lp_security() >= SEC_USER) { +#if (GUEST_SESSSETUP == 0) + return(ERROR(ERRSRV,ERRbadpw)); +#endif +#if (GUEST_SESSSETUP == 1) + if (Get_Pwnam(user,True)) + return(ERROR(ERRSRV,ERRbadpw)); +#endif + } + if (*smb_apasswd || !Get_Pwnam(user,True)) + strcpy(user,lp_guestaccount(-1)); + DEBUG(3,("Registered username %s for guest access\n",user)); + guest = True; + } + } + + if (!Get_Pwnam(user,True)) { + DEBUG(3,("No such user %s - using guest account\n",user)); + strcpy(user,lp_guestaccount(-1)); + guest = True; + } + + if (!strequal(user,lp_guestaccount(-1)) && + lp_servicenumber(user) < 0) + { + int homes = lp_servicenumber(HOMES_NAME); + char *home = get_home_dir(user); + if (homes >= 0 && home) + lp_add_home(user,homes,home); + } + + + /* it's ok - setup a reply */ + if (Protocol < PROTOCOL_NT1) { + outsize = set_message(outbuf,3,0,True); + } else { + char *p; + outsize = set_message(outbuf,3,3,True); + p = smb_buf(outbuf); + strcpy(p,"Unix"); p = skip_string(p,1); + strcpy(p,"Samba "); strcat(p,VERSION); p = skip_string(p,1); + strcpy(p,my_workgroup()); p = skip_string(p,1); + outsize = set_message(outbuf,3,PTR_DIFF(p,smb_buf(outbuf)),False); + /* perhaps grab OS version here?? */ + } + + /* Set the correct uid in the outgoing and incoming packets + We will use this on future requests to determine which + user we should become. + */ + { + struct passwd *pw = Get_Pwnam(user,False); + if (!pw) { + DEBUG(1,("Username %s is invalid on this system\n",user)); + return(ERROR(ERRSRV,ERRbadpw)); + } + gid = pw->pw_gid; + SSVAL(outbuf,smb_uid,(uint16)pw->pw_uid); + SSVAL(inbuf,smb_uid,(uint16)pw->pw_uid); + } + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + + if (guest) + SSVAL(outbuf,smb_vwv2,1); + + /* register the name and uid as being validated, so further connections + to a uid can get through without a password, on the same VC */ + register_uid(SVAL(inbuf,smb_uid),gid,user,guest); + + maxxmit = MIN(maxxmit,smb_bufsize); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + return(outsize); +} + + +/**************************************************************************** + reply to a chkpth +****************************************************************************/ +int reply_chkpth(char *inbuf,char *outbuf) +{ + int outsize = 0; + int cnum,mode; + pstring name; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf) + 1); + unix_convert(name,cnum); + + mode = SVAL(inbuf,smb_vwv0); + + if (check_name(name,cnum)) + ok = directory_exist(name,NULL); + + if (!ok) + return(ERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s chkpth %s cnum=%d mode=%d\n",timestring(),name,cnum,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a getatr +****************************************************************************/ +int reply_getatr(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int outsize = 0; + struct stat sbuf; + BOOL ok = False; + int mode=0; + uint32 size=0; + time_t mtime=0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum); + + /* dos smetimes asks for a stat of "" - it returns a "hidden directory" + under WfWg - weird! */ + if (! (*fname)) + { + mode = aHIDDEN | aDIR; + if (!CAN_WRITE(cnum)) mode |= aRONLY; + size = 0; + mtime = 0; + ok = True; + } + else + if (check_name(fname,cnum)) + { + if (sys_stat(fname,&sbuf) == 0) + { + mode = dos_mode(cnum,fname,&sbuf); + size = sbuf.st_size; + mtime = sbuf.st_mtime; + if (mode & aDIR) + size = 0; + ok = True; + } + else + DEBUG(3,("stat of %s failed (%s)\n",fname,strerror(errno))); + } + + if (!ok) + return(UNIXERROR(ERRDOS,ERRbadfile)); + + outsize = set_message(outbuf,10,0,True); + + SSVAL(outbuf,smb_vwv0,mode); + put_dos_date3(outbuf,smb_vwv1,mtime); + SIVAL(outbuf,smb_vwv3,size); + + if (Protocol >= PROTOCOL_NT1) { + char *p = strrchr(fname,'/'); + uint16 flg2 = SVAL(outbuf,smb_flg2); + if (!p) p = fname; + if (!is_8_3(fname)) + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ + } + + DEBUG(3,("%s getatr name=%s mode=%d size=%d\n",timestring(),fname,mode,size)); + + return(outsize); +} + + +/**************************************************************************** + reply to a setatr +****************************************************************************/ +int reply_setatr(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int outsize = 0; + BOOL ok=False; + int mode; + time_t mtime; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(fname,smb_buf(inbuf) + 1); + unix_convert(fname,cnum); + + mode = SVAL(inbuf,smb_vwv0); + mtime = make_unix_date3(inbuf+smb_vwv1); + + if (directory_exist(fname,NULL)) + mode |= aDIR; + if (check_name(fname,cnum)) + ok = (dos_chmod(cnum,fname,mode,NULL) == 0); + if (ok) + ok = set_filetime(fname,mtime); + + if (!ok) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s setatr name=%s mode=%d\n",timestring(),fname,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a dskattr +****************************************************************************/ +int reply_dskattr(char *inbuf,char *outbuf) +{ + int cnum; + int outsize = 0; + int dfree,dsize,bsize; + + cnum = SVAL(inbuf,smb_tid); + + sys_disk_free(".",&bsize,&dfree,&dsize); + + outsize = set_message(outbuf,5,0,True); + + SSVAL(outbuf,smb_vwv0,dsize); + SSVAL(outbuf,smb_vwv1,bsize/512); + SSVAL(outbuf,smb_vwv2,512); + SSVAL(outbuf,smb_vwv3,dfree); + + DEBUG(3,("%s dskattr cnum=%d dfree=%d\n",timestring(),cnum,dfree)); + + return(outsize); +} + + +/**************************************************************************** + reply to a search + Can be called from SMBsearch, SMBffirst or SMBfunique. +****************************************************************************/ +int reply_search(char *inbuf,char *outbuf) +{ + pstring mask; + pstring directory; + pstring fname; + int size,mode; + time_t date; + int dirtype; + int cnum; + int outsize = 0; + int numentries = 0; + BOOL finished = False; + int maxentries; + int i; + char *p; + BOOL ok = False; + int status_len; + char *path; + char status[21]; + int dptr_num= -1; + BOOL check_descend = False; + BOOL expect_close = False; + BOOL can_open = True; + + *mask = *directory = *fname = 0; + + /* If we were called as SMBffirst then we must expect close. */ + if(CVAL(inbuf,smb_com) == SMBffirst) + expect_close = True; + + cnum = SVAL(inbuf,smb_tid); + + outsize = set_message(outbuf,1,3,True); + maxentries = SVAL(inbuf,smb_vwv0); + dirtype = SVAL(inbuf,smb_vwv1); + path = smb_buf(inbuf) + 1; + status_len = SVAL(smb_buf(inbuf),3 + strlen(path)); + + + /* dirtype &= ~aDIR; */ + + DEBUG(5,("path=%s status_len=%d\n",path,status_len)); + + + if (status_len == 0) + { + pstring dir2; + + strcpy(directory,smb_buf(inbuf)+1); + strcpy(dir2,smb_buf(inbuf)+1); + unix_convert(directory,cnum); + unix_format(dir2); + + if (!check_name(directory,cnum)) + can_open = False; + + p = strrchr(dir2,'/'); + if (p == NULL) + {strcpy(mask,dir2);*dir2 = 0;} + else + {*p = 0;strcpy(mask,p+1);} + + p = strrchr(directory,'/'); + if (!p) + *directory = 0; + else + *p = 0; + + if (strlen(directory) == 0) + strcpy(directory,"./"); + bzero(status,21); + CVAL(status,0) = dirtype; + } + else + { + memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + memcpy(mask,status+1,11); + mask[11] = 0; + dirtype = CVAL(status,0) & 0x1F; + Connections[cnum].dirptr = dptr_fetch(status+12,&dptr_num); + if (!Connections[cnum].dirptr) + goto SearchEmpty; + string_set(&Connections[cnum].dirpath,dptr_path(dptr_num)); + if (!case_sensitive) + strnorm(mask); + } + + /* turn strings of spaces into a . */ + { + trim_string(mask,NULL," "); + if ((p = strrchr(mask,' '))) + { + fstring ext; + strcpy(ext,p+1); + *p = 0; + trim_string(mask,NULL," "); + strcat(mask,"."); + strcat(mask,ext); + } + } + + { + for (p=mask; *p; p++) + { + if (*p != '?' && *p != '*' && !isdoschar(*p)) + { + DEBUG(5,("Invalid char [%c] in search mask?\n",*p)); + *p = '?'; + } + } + } + + if (!strchr(mask,'.') && strlen(mask)>8) + { + fstring tmp; + strcpy(tmp,&mask[8]); + mask[8] = '.'; + mask[9] = 0; + strcat(mask,tmp); + } + + DEBUG(5,("mask=%s directory=%s\n",mask,directory)); + + if (can_open) + { + p = smb_buf(outbuf) + 3; + + ok = True; + + if (status_len == 0) + { + dptr_num = dptr_create(cnum,directory,expect_close,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) + return(ERROR(ERRDOS,ERRnofids)); + } + + DEBUG(4,("dptr_num is %d\n",dptr_num)); + + if (ok) + { + if ((dirtype&0x1F) == aVOLID) + { + memcpy(p,status,21); + make_dir_struct(p,"???????????",volume_label(SNUM(cnum)),0,aVOLID,0); + dptr_fill(p+12,dptr_num); + if (dptr_zero(p+12) && (status_len==0)) + numentries = 1; + else + numentries = 0; + p += DIR_STRUCT_SIZE; + } + else + { + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); + if (in_list(Connections[cnum].dirpath, + lp_dontdescend(SNUM(cnum)),True)) + check_descend = True; + + for (i=numentries;(i<maxentries) && !finished;i++) + { + finished = + !get_dir_entry(cnum,mask,dirtype,fname,&size,&mode,&date,check_descend); + if (!finished) + { + memcpy(p,status,21); + make_dir_struct(p,mask,fname,size,mode,date); + dptr_fill(p+12,dptr_num); + numentries++; + } + p += DIR_STRUCT_SIZE; + } + } + } + } + + + SearchEmpty: + + if (numentries == 0 || !ok) + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + } + + /* If we were called as SMBffirst with smb_search_id == NULL + and no entries were found then return error and close dirptr + (X/Open spec) */ + + if(ok && expect_close && numentries == 0 && status_len == 0) + { + CVAL(outbuf,smb_rcls) = ERRDOS; + SSVAL(outbuf,smb_err,ERRnofiles); + /* Also close the dptr - we know it's gone */ + dptr_close(dptr_num); + } + + /* If we were called as SMBfunique, then we can close the dirptr now ! */ + if(dptr_num >= 0 && CVAL(inbuf,smb_com) == SMBfunique) + dptr_close(dptr_num); + + SSVAL(outbuf,smb_vwv0,numentries); + SSVAL(outbuf,smb_vwv1,3 + numentries * DIR_STRUCT_SIZE); + CVAL(smb_buf(outbuf),0) = 5; + SSVAL(smb_buf(outbuf),1,numentries*DIR_STRUCT_SIZE); + + if (Protocol >= PROTOCOL_NT1) { + uint16 flg2 = SVAL(outbuf,smb_flg2); + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ + } + + outsize += DIR_STRUCT_SIZE*numentries; + smb_setlen(outbuf,outsize - 4); + + if ((! *directory) && dptr_path(dptr_num)) + sprintf(directory,"(%s)",dptr_path(dptr_num)); + + DEBUG(4,("%s %s mask=%s path=%s cnum=%d dtype=%d nument=%d of %d\n", + timestring(), + smb_fn_name(CVAL(inbuf,smb_com)), + mask,directory,cnum,dirtype,numentries,maxentries)); + + return(outsize); +} + + +/**************************************************************************** + reply to a fclose (stop directory search) +****************************************************************************/ +int reply_fclose(char *inbuf,char *outbuf) +{ + int cnum; + int outsize = 0; + int status_len; + char *path; + char status[21]; + int dptr_num= -1; + + cnum = SVAL(inbuf,smb_tid); + + outsize = set_message(outbuf,1,0,True); + path = smb_buf(inbuf) + 1; + status_len = SVAL(smb_buf(inbuf),3 + strlen(path)); + + + if (status_len == 0) + return(ERROR(ERRSRV,ERRsrverror)); + + memcpy(status,smb_buf(inbuf) + 1 + strlen(path) + 4,21); + + if(dptr_fetch(status+12,&dptr_num)) { + /* Close the dptr - we know it's gone */ + dptr_close(dptr_num); + } + + SSVAL(outbuf,smb_vwv0,0); + + DEBUG(3,("%s search close cnum=%d\n",timestring(),cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to an open +****************************************************************************/ +int reply_open(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum; + int fnum = -1; + int outsize = 0; + int fmode=0; + int share_mode; + int size = 0; + time_t mtime=0; + int unixmode; + int rmode=0; + struct stat sbuf; + + cnum = SVAL(inbuf,smb_tid); + + share_mode = SVAL(inbuf,smb_vwv0); + + strcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + unixmode = unix_mode(cnum,aARCH); + + open_file_shared(fnum,cnum,fname,share_mode,3,unixmode,&rmode,NULL); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (fstat(Files[fnum].fd,&sbuf) != 0) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + size = sbuf.st_size; + fmode = dos_mode(cnum,fname,&sbuf); + mtime = sbuf.st_mtime; + + if (fmode & aDIR) { + DEBUG(3,("attempt to open a directory %s\n",fname)); + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + outsize = set_message(outbuf,7,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + SSVAL(outbuf,smb_vwv1,fmode); + put_dos_date3(outbuf,smb_vwv2,mtime); + SIVAL(outbuf,smb_vwv4,size); + SSVAL(outbuf,smb_vwv6,rmode); + + return(outsize); +} + + +/**************************************************************************** + reply to an open and X +****************************************************************************/ +int reply_open_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + pstring fname; + int cnum = SVAL(inbuf,smb_tid); + int fnum = -1; + int outsize = 0; + int openmode = 0; + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int smb_mode = SVAL(inbuf,smb_vwv3); + int smb_attr = SVAL(inbuf,smb_vwv5); +#if 0 + int open_flags = SVAL(inbuf,smb_vwv2); + int smb_sattr = SVAL(inbuf,smb_vwv4); + uint32 smb_time = make_unix_date3(inbuf+smb_vwv6); +#endif + int smb_ofun = SVAL(inbuf,smb_vwv8); + int unixmode; + int size=0,fmode=0,mtime=0,rmode=0; + struct stat sbuf; + int smb_action = 0; + + /* XXXX we need to handle passed times, sattr and flags */ + + strcpy(fname,smb_buf(inbuf)); + unix_convert(fname,cnum); + + /* now add create and trunc bits */ + if (smb_ofun & 0x10) + openmode |= O_CREAT; + if ((smb_ofun & 0x3) == 2) + openmode |= O_TRUNC; + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + unixmode = unix_mode(cnum,smb_attr | aARCH); + + open_file_shared(fnum,cnum,fname,smb_mode,smb_ofun,unixmode, + &rmode,&smb_action); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (fstat(Files[fnum].fd,&sbuf) != 0) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + size = sbuf.st_size; + fmode = dos_mode(cnum,fname,&sbuf); + mtime = sbuf.st_mtime; + if (fmode & aDIR) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + outsize = set_message(outbuf,15,0,True); + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + SSVAL(outbuf,smb_vwv2,fnum); + SSVAL(outbuf,smb_vwv3,fmode); + put_dos_date3(outbuf,smb_vwv4,mtime); + SIVAL(outbuf,smb_vwv6,size); + SSVAL(outbuf,smb_vwv8,rmode); + SSVAL(outbuf,smb_vwv11,smb_action); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBulogoffX +****************************************************************************/ +int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int uid = SVAL(inbuf,smb_uid); + + invalidate_uid(uid); + + outsize = set_message(outbuf,2,0,True); + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(chain_size+outsize)-4); + + DEBUG(3,("%s ulogoffX uid=%d\n",timestring(),uid)); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + + return(outsize); +} + + +/**************************************************************************** + reply to a mknew +****************************************************************************/ +int reply_mknew(char *inbuf,char *outbuf) +{ + pstring fname; + int cnum,com; + int fnum = -1; + int outsize = 0; + int createmode; + mode_t unixmode; + + com = SVAL(inbuf,smb_com); + cnum = SVAL(inbuf,smb_tid); + + createmode = SVAL(inbuf,smb_vwv0); + strcpy(fname,smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + if (createmode & aVOLID) + { + DEBUG(0,("Attempt to create file (%s) with volid set - please report this\n",fname)); + } + + unixmode = unix_mode(cnum,createmode); + + if (com == SMBmknew && file_exist(fname,NULL)) + return(ERROR(ERRDOS,ERRfilexists)); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + open_file(fnum,cnum,fname,O_RDWR | O_CREAT | O_TRUNC,unixmode); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + + DEBUG(2,("new file %s\n",fname)); + DEBUG(3,("%s mknew %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a create temporary file +****************************************************************************/ +int reply_ctemp(char *inbuf,char *outbuf) +{ + pstring fname; + pstring fname2; + int cnum; + int fnum = -1; + int outsize = 0; + int createmode; + mode_t unixmode; + + cnum = SVAL(inbuf,smb_tid); + createmode = SVAL(inbuf,smb_vwv0); + sprintf(fname,"%s/TMXXXXXX",smb_buf(inbuf)+1); + unix_convert(fname,cnum); + + unixmode = unix_mode(cnum,createmode); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + strcpy(fname2,(char *)mktemp(fname)); + + open_file(fnum,cnum,fname2,O_RDWR | O_CREAT | O_TRUNC,unixmode); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,2 + strlen(fname2),True); + SSVAL(outbuf,smb_vwv0,fnum); + CVAL(smb_buf(outbuf),0) = 4; + strcpy(smb_buf(outbuf) + 1,fname2); + + DEBUG(2,("created temp file %s\n",fname2)); + DEBUG(3,("%s ctemp %s fd=%d fnum=%d cnum=%d dmode=%d umode=%o\n",timestring(),fname2,Files[fnum].fd,fnum,cnum,createmode,unixmode)); + + return(outsize); +} + + +/******************************************************************* +check if a user is allowed to delete a file +********************************************************************/ +static BOOL can_delete(char *fname,int cnum,int dirtype) +{ + struct stat sbuf; + int fmode; + + if (!CAN_WRITE(cnum)) return(False); + + if (sys_lstat(fname,&sbuf) != 0) return(False); + fmode = dos_mode(cnum,fname,&sbuf); + if (fmode & aDIR) return(False); + if (fmode & aRONLY) return(False); + if ((fmode & ~dirtype) & (aHIDDEN | aSYSTEM)) + return(False); + if (!check_file_sharing(cnum,fname)) return(False); + return(True); +} + +/**************************************************************************** + reply to a unlink +****************************************************************************/ +int reply_unlink(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + int dirtype; + pstring directory; + pstring mask; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + dirtype = SVAL(inbuf,smb_vwv0); + + strcpy(name,smb_buf(inbuf) + 1); + + DEBUG(3,("reply_unlink : %s\n",name)); + + unix_convert(name,cnum); + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (can_delete(directory,cnum,dirtype) && !sys_unlink(directory)) count++; + if (!count) exists = file_exist(directory,NULL); + } else { + void *dirptr = NULL; + char *dname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + if (!can_delete(fname,cnum,dirtype)) continue; + if (!sys_unlink(fname)) count++; + DEBUG(3,("reply_unlink : doing unlink on %s\n",fname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,0,0,True); + + return(outsize); +} + + +/**************************************************************************** + reply to a readbraw (core+ protocol) +****************************************************************************/ +int reply_readbraw(char *inbuf, char *outbuf) +{ + int cnum,maxcount,mincount,fnum; + int nread = 0; + int startpos; + char *header = outbuf; + int ret=0; + int fd; + char *fname; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); + mincount = SVAL(inbuf,smb_vwv4); + + /* ensure we don't overrun the packet size */ + maxcount = MIN(65535,maxcount); + maxcount = MAX(mincount,maxcount); + + if (!FNUM_OK(fnum,cnum) || !Files[fnum].can_read) + { + DEBUG(3,("fnum %d not open in readbraw - cache prime?\n",fnum)); + _smb_setlen(header,0); + transfer_file(0,Client,0,header,4,0); + return(-1); + } + else + { + fd = Files[fnum].fd; + fname = Files[fnum].name; + } + + + if (!is_locked(fnum,cnum,maxcount,startpos)) + { + int size = Files[fnum].size; + int sizeneeded = startpos + maxcount; + + if (size < sizeneeded) { + struct stat st; + if (fstat(Files[fnum].fd,&st) == 0) + size = st.st_size; + if (!Files[fnum].can_write) + Files[fnum].size = size; + } + + nread = MIN(maxcount,size - startpos); + } + + if (nread < mincount) + nread = 0; + + DEBUG(3,("%s readbraw fnum=%d cnum=%d start=%d max=%d min=%d nread=%d\n", + timestring(), + fnum,cnum,startpos, + maxcount,mincount,nread)); + +#if UNSAFE_READRAW + { + int predict=0; + _smb_setlen(header,nread); + + if (!Files[fnum].can_write) + predict = read_predict(fd,startpos,header+4,NULL,nread); + + if ((nread-predict) > 0) + seek_file(fnum,startpos + predict); + + ret = transfer_file(fd,Client,nread-predict,header,4+predict, + startpos+predict); + } + + if (ret != nread+4) + DEBUG(0,("ERROR: file read failure on %s at %d for %d bytes (%d)\n", + fname,startpos,nread,ret)); + +#else + ret = read_file(fnum,header+4,startpos,nread,nread,-1,False); + if (ret < mincount) ret = 0; + + _smb_setlen(header,ret); + transfer_file(0,Client,0,header,4+ret,0); +#endif + + DEBUG(5,("readbraw finished\n")); + return -1; +} + + +/**************************************************************************** + reply to a lockread (core+ protocol) +****************************************************************************/ +int reply_lockread(char *inbuf,char *outbuf) +{ + int cnum,fnum; + int nread = -1; + char *data; + int outsize = 0; + uint32 startpos, numtoread; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + if(!do_lock( fnum, cnum, numtoread, startpos, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG(3,("%s lockread fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread)); + + return(outsize); +} + + +/**************************************************************************** + reply to a read +****************************************************************************/ +int reply_read(char *inbuf,char *outbuf) +{ + int cnum,numtoread,fnum; + int nread = 0; + char *data; + int startpos; + int outsize = 0; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + numtoread = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + + outsize = set_message(outbuf,5,3,True); + numtoread = MIN(BUFFER_SIZE-outsize,numtoread); + data = smb_buf(outbuf) + 3; + + if (is_locked(fnum,cnum,numtoread,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + if (numtoread > 0) + nread = read_file(fnum,data,startpos,numtoread,numtoread,-1,False); + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + SSVAL(outbuf,smb_vwv0,nread); + SSVAL(outbuf,smb_vwv5,nread+3); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,nread); + + DEBUG(3,("%s read fnum=%d cnum=%d num=%d nread=%d\n",timestring(),fnum,cnum,numtoread,nread)); + + return(outsize); +} + + +/**************************************************************************** + reply to a read and X +****************************************************************************/ +int reply_read_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint32 smb_offs = IVAL(inbuf,smb_vwv3); + int smb_maxcnt = SVAL(inbuf,smb_vwv5); + int smb_mincnt = SVAL(inbuf,smb_vwv6); + int cnum; + int nread = -1; + char *data; + int outsize = 0; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + outsize = set_message(outbuf,12,0,True); + data = smb_buf(outbuf); + + if (is_locked(fnum,cnum,smb_maxcnt,smb_offs)) + return(ERROR(ERRDOS,ERRlock)); + nread = read_file(fnum,data,smb_offs,smb_maxcnt,smb_maxcnt,-1,False); + ok = True; + + if (nread < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize += nread; + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + SSVAL(outbuf,smb_vwv5,nread); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf) + chain_size); + SSVAL(smb_buf(outbuf),-2,nread); + + DEBUG(3,("%s readX fnum=%d cnum=%d min=%d max=%d nread=%d com2=%d off2=%d\n", + timestring(),fnum,cnum, + smb_mincnt,smb_maxcnt,nread,smb_com2,smb_off2)); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a writebraw (core+ or LANMAN1.0 protocol) +****************************************************************************/ +int reply_writebraw(char *inbuf,char *outbuf) +{ + int nwritten=0; + int total_written=0; + int numtowrite=0; + int cnum,fnum; + int outsize = 0; + long startpos; + char *data=NULL; + BOOL write_through; + int tcount; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + tcount = IVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + + /* We have to deal with slightly different formats depending + on whether we are using the core+ or lanman1.0 protocol */ + if(Protocol <= PROTOCOL_COREPLUS) { + numtowrite = SVAL(smb_buf(inbuf),-2); + data = smb_buf(inbuf); + } else { + numtowrite = SVAL(inbuf,smb_vwv10); + data = smb_base(inbuf) + SVAL(inbuf, smb_vwv11); + } + + /* force the error type */ + CVAL(inbuf,smb_com) = SMBwritec; + CVAL(outbuf,smb_com) = SMBwritec; + + if (is_locked(fnum,cnum,tcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + if (seek_file(fnum,startpos) != startpos) + DEBUG(0,("couldn't seek to %d in writebraw\n",startpos)); + + if (numtowrite>0) + nwritten = write_file(fnum,data,numtowrite); + + DEBUG(3,("%s writebraw1 fnum=%d cnum=%d start=%d num=%d wrote=%d sync=%d\n", + timestring(),fnum,cnum,startpos,numtowrite,nwritten,write_through)); + + if (nwritten < numtowrite) + return(UNIXERROR(ERRHRD,ERRdiskfull)); + + total_written = nwritten; + + /* Return a message to the redirector to tell it + to send more bytes */ + CVAL(outbuf,smb_com) = SMBwritebraw; + SSVALS(outbuf,smb_vwv0,-1); + outsize = set_message(outbuf,Protocol>PROTOCOL_COREPLUS?1:0,0,True); + send_smb(Client,outbuf); + + /* Now read the raw data into the buffer and write it */ + if(read_smb_length(Client,inbuf,0) == -1) { + exit_server("secondary writebraw failed"); + } + + /* Even though this is not an smb message, smb_len + returns the generic length of an smb message */ + numtowrite = smb_len(inbuf); + + if (tcount > nwritten+numtowrite) { + DEBUG(3,("Client overestimated the write %d %d %d\n", + tcount,nwritten,numtowrite)); + } + + nwritten = transfer_file(Client,Files[fnum].fd,numtowrite,NULL,0, + startpos+nwritten); + total_written += nwritten; + + /* Set up outbuf to return the correct return */ + outsize = set_message(outbuf,1,0,True); + CVAL(outbuf,smb_com) = SMBwritec; + SSVAL(outbuf,smb_vwv0,total_written); + + if (nwritten < numtowrite) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + if (lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + DEBUG(3,("%s writebraw2 fnum=%d cnum=%d start=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,startpos,numtowrite,total_written)); + + /* we won't return a status if write through is not selected - this + follows what WfWg does */ + if (!write_through && total_written==tcount) + return(-1); + + return(outsize); +} + + +/**************************************************************************** + reply to a writeunlock (core+) +****************************************************************************/ +int reply_writeunlock(char *inbuf,char *outbuf) +{ + int cnum,fnum; + int nwritten = -1; + int outsize = 0; + char *data; + uint32 numtowrite,startpos; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + /* The special X/Open SMB protocol handling of + zero length writes is *NOT* done for + this call */ + if(numtowrite == 0) + nwritten = 0; + else + nwritten = write_file(fnum,data,numtowrite); + + if (lp_syncalways(SNUM(cnum))) + sync_file(fnum); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if(!do_unlock(fnum, cnum, numtowrite, startpos, &eclass, &ecode)) + return(ERROR(eclass,ecode)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + DEBUG(3,("%s writeunlock fnum=%d cnum=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,numtowrite,nwritten)); + + return(outsize); +} + + +/**************************************************************************** + reply to a write +****************************************************************************/ +int reply_write(char *inbuf,char *outbuf,int dum1,int dum2) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int startpos; + char *data; + + dum1 = dum2 = 0; + + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + data = smb_buf(inbuf) + 3; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + /* X/Open SMB protocol says that if smb_vwv1 is + zero then the file size should be extended or + truncated to the size given in smb_vwv[2-3] */ + if(numtowrite == 0) + nwritten = set_filelen(Files[fnum].fd, startpos); + else + nwritten = write_file(fnum,data,numtowrite); + + if (lp_syncalways(SNUM(cnum))) + sync_file(fnum); + + if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + + if (nwritten < numtowrite) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("%s write fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,numtowrite,nwritten)); + + return(outsize); +} + + +/**************************************************************************** + reply to a write and X +****************************************************************************/ +int reply_write_and_X(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint32 smb_offs = IVAL(inbuf,smb_vwv3); + int smb_dsize = SVAL(inbuf,smb_vwv10); + int smb_doff = SVAL(inbuf,smb_vwv11); + BOOL write_through = BITSETW(inbuf+smb_vwv7,0); + int cnum; + int nwritten = -1; + int outsize = 0; + char *data; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + data = smb_base(inbuf) + smb_doff; + + if (is_locked(fnum,cnum,smb_dsize,smb_offs)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,smb_offs); + + /* X/Open SMB protocol says that, unlike SMBwrite + if the length is zero then NO truncation is + done, just a write of zero. To truncate a file, + use SMBwrite. */ + if(smb_dsize == 0) + nwritten = 0; + else + nwritten = write_file(fnum,data,smb_dsize); + + if(((nwritten == 0) && (smb_dsize != 0))||(nwritten < 0)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,6,0,True); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + SSVAL(outbuf,smb_vwv2,nwritten); + + if (nwritten < smb_dsize) { + CVAL(outbuf,smb_rcls) = ERRHRD; + SSVAL(outbuf,smb_err,ERRdiskfull); + } + + DEBUG(3,("%s writeX fnum=%d cnum=%d num=%d wrote=%d\n",timestring(),fnum,cnum,smb_dsize,nwritten)); + + chain_fnum = fnum; + + if (lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a lseek +****************************************************************************/ +int reply_lseek(char *inbuf,char *outbuf) +{ + int cnum,fnum; + uint32 startpos; + int32 res= -1; + int mode,umode; + int outsize = 0; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + mode = SVAL(inbuf,smb_vwv1) & 3; + startpos = IVAL(inbuf,smb_vwv2); + + switch (mode & 3) + { + case 0: umode = SEEK_SET; break; + case 1: umode = SEEK_CUR; break; + case 2: umode = SEEK_END; break; + default: + umode = SEEK_SET; break; + } + + res = lseek(Files[fnum].fd,startpos,umode); + Files[fnum].pos = res; + + outsize = set_message(outbuf,2,0,True); + SIVALS(outbuf,smb_vwv0,res); + + DEBUG(3,("%s lseek fnum=%d cnum=%d ofs=%d mode=%d\n",timestring(),fnum,cnum,startpos,mode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a flush +****************************************************************************/ +int reply_flush(char *inbuf,char *outbuf) +{ + int cnum, fnum; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + if (fnum != 0xFFFF) { + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + } + + if (fnum == 0xFFFF) + { + int i; + for (i=0;i<MAX_OPEN_FILES;i++) + if (OPEN_FNUM(i)) + sync_file(i); + } + else + sync_file(fnum); + + DEBUG(3,("%s flush fnum=%d\n",timestring(),fnum)); + return(outsize); +} + + +/**************************************************************************** + reply to a exit +****************************************************************************/ +int reply_exit(char *inbuf,char *outbuf) +{ + int outsize = set_message(outbuf,0,0,True); + DEBUG(3,("%s exit\n",timestring())); + + return(outsize); +} + + +/**************************************************************************** + reply to a close +****************************************************************************/ +int reply_close(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = 0; + time_t mtime; + int32 eclass = 0, err = 0; + + outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + + fnum = GETFNUM(inbuf,smb_vwv0); + CHECK_FNUM(fnum,cnum); + + if(HAS_CACHED_ERROR(fnum)) { + eclass = Files[fnum].wbmpx_ptr->wr_errclass; + err = Files[fnum].wbmpx_ptr->wr_error; + } + + mtime = make_unix_date3(inbuf+smb_vwv1); + + close_file(fnum); + + /* try and set the date */ + set_filetime(Files[fnum].name,mtime); + + /* We have a cached error */ + if(eclass || err) + return(ERROR(eclass,err)); + + DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n", + timestring(),Files[fnum].fd,fnum,cnum, + Connections[cnum].num_files_open)); + + return(outsize); +} + + +/**************************************************************************** + reply to a writeclose (Core+ protocol) +****************************************************************************/ +int reply_writeclose(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int startpos; + char *data; + time_t mtime; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + mtime = make_unix_date3(inbuf+smb_vwv4); + data = smb_buf(inbuf) + 1; + + if (is_locked(fnum,cnum,numtowrite,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + + nwritten = write_file(fnum,data,numtowrite); + + close_file(fnum); + + set_filetime(Files[fnum].name,mtime); + + DEBUG(3,("%s writeclose fnum=%d cnum=%d num=%d wrote=%d (numopen=%d)\n", + timestring(),fnum,cnum,numtowrite,nwritten, + Connections[cnum].num_files_open)); + + if (nwritten <= 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,1,0,True); + + SSVAL(outbuf,smb_vwv0,nwritten); + return(outsize); +} + + +/**************************************************************************** + reply to a lock +****************************************************************************/ +int reply_lock(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + uint32 count,offset; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + count = IVAL(inbuf,smb_vwv1); + offset = IVAL(inbuf,smb_vwv3); + + DEBUG(3,("%s lock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + + if(!do_lock( fnum, cnum, count, offset, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + return(outsize); +} + + +/**************************************************************************** + reply to a unlock +****************************************************************************/ +int reply_unlock(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + uint32 count,offset; + int eclass; + uint32 ecode; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + count = IVAL(inbuf,smb_vwv1); + offset = IVAL(inbuf,smb_vwv3); + + if(!do_unlock(fnum, cnum, count, offset, &eclass, &ecode)) + return (ERROR(eclass,ecode)); + + DEBUG(3,("%s unlock fd=%d fnum=%d cnum=%d ofs=%d cnt=%d\n",timestring(),Files[fnum].fd,fnum,cnum,offset,count)); + + return(outsize); +} + + +/**************************************************************************** + reply to a tdis +****************************************************************************/ +int reply_tdis(char *inbuf,char *outbuf) +{ + int cnum, uid; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + uid = SVAL(inbuf,smb_uid); + + Connections[cnum].used = False; + + close_cnum(cnum,uid); + + DEBUG(3,("%s tdis cnum=%d\n",timestring(),cnum)); + + return outsize; +} + + + +/**************************************************************************** + reply to a echo +****************************************************************************/ +int reply_echo(char *inbuf,char *outbuf) +{ + int cnum; + int smb_reverb = SVAL(inbuf,smb_vwv0); + int seq_num; + int data_len = smb_buflen(inbuf); + int outsize = set_message(outbuf,1,data_len,True); + + cnum = SVAL(inbuf,smb_tid); + + if (cnum != 0xFFFF && !OPEN_CNUM(cnum)) + { + DEBUG(4,("Invalid cnum in echo (%d)\n",cnum)); + return(ERROR(ERRSRV,ERRinvnid)); + } + + /* copy any incoming data back out */ + if (data_len > 0) + memcpy(smb_buf(outbuf),smb_buf(inbuf),data_len); + + if (smb_reverb > 100) + { + DEBUG(0,("large reverb (%d)?? Setting to 100\n",smb_reverb)); + smb_reverb = 100; + } + + for (seq_num =1 ; seq_num <= smb_reverb ; seq_num++) + { + SSVAL(outbuf,smb_vwv0,seq_num); + + smb_setlen(outbuf,outsize - 4); + + send_smb(Client,outbuf); + } + + DEBUG(3,("%s echo %d times cnum=%d\n",timestring(),smb_reverb,cnum)); + + return -1; +} + + +/**************************************************************************** + reply to a printopen +****************************************************************************/ +int reply_printopen(char *inbuf,char *outbuf) +{ + pstring fname; + pstring fname2; + int cnum; + int fnum = -1; + int outsize = 0; + + *fname = *fname2 = 0; + + cnum = SVAL(inbuf,smb_tid); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + { + pstring s; + char *p; + StrnCpy(s,smb_buf(inbuf)+1,sizeof(pstring)-1); + p = s; + while (*p) + { + if (!(isalnum(*p) || strchr("._-",*p))) + *p = 'X'; + p++; + } + + if (strlen(s) > 10) s[10] = 0; + + sprintf(fname,"%s.XXXXXX",s); + } + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + strcpy(fname2,(char *)mktemp(fname)); + + if (!check_name(fname2,cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + open_file(fnum,cnum,fname2,O_WRONLY | O_CREAT | O_TRUNC, + unix_mode(cnum,0)); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + /* force it to be a print file */ + Files[fnum].print_file = True; + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,fnum); + + DEBUG(3,("%s openprint %s fd=%d fnum=%d cnum=%d\n",timestring(),fname2,Files[fnum].fd,fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a printclose +****************************************************************************/ +int reply_printclose(char *inbuf,char *outbuf) +{ + int fnum,cnum; + int outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + close_file(fnum); + + DEBUG(3,("%s printclose fd=%d fnum=%d cnum=%d\n",timestring(),Files[fnum].fd,fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a printqueue +****************************************************************************/ +int reply_printqueue(char *inbuf,char *outbuf) +{ + int cnum, uid; + int outsize = set_message(outbuf,2,3,True); + int max_count = SVAL(inbuf,smb_vwv0); + int start_index = SVAL(inbuf,smb_vwv1); + + cnum = SVAL(inbuf,smb_tid); + uid = SVAL(inbuf,smb_uid); + +/* allow checking the queue for anyone */ +#if 0 + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); +#endif + + SSVAL(outbuf,smb_vwv0,0); + SSVAL(outbuf,smb_vwv1,0); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,0); + + DEBUG(3,("%s printqueue cnum=%d start_index=%d max_count=%d\n", + timestring(),cnum,start_index,max_count)); + + if (!OPEN_CNUM(cnum) || !Connections[cnum].printer) + { + int i; + cnum = -1; + + for (i=0;i<MAX_CONNECTIONS;i++) + if (CAN_PRINT(i) && Connections[i].printer) + cnum = i; + + if (cnum == -1) + for (i=0;i<MAX_CONNECTIONS;i++) + if (OPEN_CNUM(i)) + cnum = i; + + if (!OPEN_CNUM(cnum)) + return(ERROR(ERRSRV,ERRinvnid)); + + DEBUG(5,("connection not open or not a printer, using cnum %d\n",cnum)); + } + + if (!become_user(cnum,uid)) + return(ERROR(ERRSRV,ERRinvnid)); + + { + print_queue_struct *queue = NULL; + char *p = smb_buf(outbuf) + 3; + int count = get_printqueue(SNUM(cnum),cnum,&queue,NULL); + int num_to_get = ABS(max_count); + int first = (max_count>0?start_index:start_index+max_count+1); + int i; + + if (first >= count) + num_to_get = 0; + else + num_to_get = MIN(num_to_get,count-first); + + + for (i=first;i<first+num_to_get;i++) + { + put_dos_date2(p,0,queue[i].time); + CVAL(p,4) = (queue[i].status==LPQ_PRINTING?2:3); + SSVAL(p,5,queue[i].job); + SIVAL(p,7,queue[i].size); + CVAL(p,11) = 0; + StrnCpy(p+12,queue[i].user,16); + p += 28; + } + + if (count > 0) + { + outsize = set_message(outbuf,2,28*count+3,False); + SSVAL(outbuf,smb_vwv0,count); + SSVAL(outbuf,smb_vwv1,(max_count>0?first+count:first-1)); + CVAL(smb_buf(outbuf),0) = 1; + SSVAL(smb_buf(outbuf),1,28*count); + } + + if (queue) free(queue); + + DEBUG(3,("%d entries returned in queue\n",count)); + } + + return(outsize); +} + + +/**************************************************************************** + reply to a printwrite +****************************************************************************/ +int reply_printwrite(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int outsize = set_message(outbuf,0,0,True); + char *data; + + cnum = SVAL(inbuf,smb_tid); + + if (!CAN_PRINT(cnum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + numtowrite = SVAL(smb_buf(inbuf),1); + data = smb_buf(inbuf) + 3; + + if (write_file(fnum,data,numtowrite) != numtowrite) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + DEBUG(3,("%s printwrite fnum=%d cnum=%d num=%d\n",timestring(),fnum,cnum,numtowrite)); + + return(outsize); +} + + +/**************************************************************************** + reply to a mkdir +****************************************************************************/ +int reply_mkdir(char *inbuf,char *outbuf) +{ + pstring directory; + int cnum; + int outsize,ret= -1; + + strcpy(directory,smb_buf(inbuf) + 1); + cnum = SVAL(inbuf,smb_tid); + unix_convert(directory,cnum); + + if (check_name(directory,cnum)) + ret = sys_mkdir(directory,unix_mode(cnum,aDIR)); + + if (ret < 0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s mkdir %s cnum=%d ret=%d\n",timestring(),directory,cnum,ret)); + + return(outsize); +} + + +/**************************************************************************** + reply to a rmdir +****************************************************************************/ +int reply_rmdir(char *inbuf,char *outbuf) +{ + pstring directory; + int cnum; + int outsize = 0; + BOOL ok = False; + + cnum = SVAL(inbuf,smb_tid); + strcpy(directory,smb_buf(inbuf) + 1); + unix_convert(directory,cnum); + + if (check_name(directory,cnum)) + { + dptr_closepath(directory,SVAL(inbuf,smb_pid)); + ok = (sys_rmdir(directory) == 0); + if (!ok) + DEBUG(3,("couldn't remove directory %s : %s\n", + directory,strerror(errno))); + } + + if (!ok) + return(UNIXERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s rmdir %s\n",timestring(),directory)); + + return(outsize); +} + + +/******************************************************************* +resolve wildcards in a filename rename +********************************************************************/ +static BOOL resolve_wildcards(char *name1,char *name2) +{ + fstring root1,root2; + fstring ext1,ext2; + char *p,*p2; + + name1 = strrchr(name1,'/'); + name2 = strrchr(name2,'/'); + + if (!name1 || !name2) return(False); + + strcpy(root1,name1); + strcpy(root2,name2); + p = strrchr(root1,'.'); + if (p) { + *p = 0; + strcpy(ext1,p+1); + } else { + strcpy(ext1,""); + } + p = strrchr(root2,'.'); + if (p) { + *p = 0; + strcpy(ext2,p+1); + } else { + strcpy(ext2,""); + } + + p = root1; + p2 = root2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else { + p2++; + } + if (*p) p++; + } + + p = ext1; + p2 = ext2; + while (*p2) { + if (*p2 == '?') { + *p2 = *p; + p2++; + } else { + p2++; + } + if (*p) p++; + } + + strcpy(name2,root2); + if (ext2[0]) { + strcat(name2,"."); + strcat(name2,ext2); + } + + return(True); +} + +/******************************************************************* +check if a user is allowed to rename a file +********************************************************************/ +static BOOL can_rename(char *fname,int cnum) +{ + struct stat sbuf; + + if (!CAN_WRITE(cnum)) return(False); + + if (sys_lstat(fname,&sbuf) != 0) return(False); + if (!check_file_sharing(cnum,fname)) return(False); + + return(True); +} + +/**************************************************************************** + reply to a mv +****************************************************************************/ +int reply_mv(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + pstring directory; + pstring mask,newname; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf) + 1); + strcpy(newname,smb_buf(inbuf) + 3 + strlen(name)); + + DEBUG(3,("reply_mv : %s -> %s\n",name,newname)); + + unix_convert(name,cnum); + unix_convert(newname,cnum); + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (resolve_wildcards(directory,newname) && + can_rename(directory,cnum) && + !file_exist(newname,NULL) && + !sys_rename(directory,newname)) count++; + if (!count) exists = file_exist(directory,NULL); + if (!count && exists && file_exist(newname,NULL)) { + exists = True; + error = 183; + } + } else { + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + if (!can_rename(fname,cnum)) continue; + strcpy(destname,newname); + + if (!resolve_wildcards(fname,destname)) continue; + + if (file_exist(destname,NULL)) { + error = 183; + continue; + } + if (!sys_rename(fname,destname)) count++; + DEBUG(3,("reply_mv : doing rename on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,0,0,True); + + return(outsize); +} + +/******************************************************************* + copy a file as part of a reply_copy + ******************************************************************/ +static BOOL copy_file(char *src,char *dest1,int cnum,int ofun, + int count,BOOL target_is_directory) +{ + int Access,action; + struct stat st; + int ret=0; + int fnum1,fnum2; + pstring dest; + + strcpy(dest,dest1); + if (target_is_directory) { + char *p = strrchr(src,'/'); + if (p) + p++; + else + p = src; + strcat(dest,"/"); + strcat(dest,p); + } + + if (!file_exist(src,&st)) return(False); + + fnum1 = find_free_file(); + if (fnum1<0) return(False); + open_file_shared(fnum1,cnum,src,(DENY_NONE<<4), + 1,0,&Access,&action); + + if (!Files[fnum1].open) return(False); + + if (!target_is_directory && count) + ofun = 1; + + fnum2 = find_free_file(); + if (fnum2<0) { + close_file(fnum1); + return(False); + } + open_file_shared(fnum2,cnum,dest,(DENY_NONE<<4)|1, + ofun,st.st_mode,&Access,&action); + + if (!Files[fnum2].open) { + close_file(fnum1); + return(False); + } + + if ((ofun&3) == 1) { + lseek(Files[fnum2].fd,0,SEEK_END); + } + + if (st.st_size) + ret = transfer_file(Files[fnum1].fd,Files[fnum2].fd,st.st_size,NULL,0,0); + + close_file(fnum1); + close_file(fnum2); + + return(ret == st.st_size); +} + + + +/**************************************************************************** + reply to a file copy. + ****************************************************************************/ +int reply_copy(char *inbuf,char *outbuf) +{ + int outsize = 0; + pstring name; + int cnum; + pstring directory; + pstring mask,newname; + char *p; + int count=0; + int error = ERRnoaccess; + BOOL has_wild; + BOOL exists=False; + int tid2 = SVAL(inbuf,smb_vwv0); + int ofun = SVAL(inbuf,smb_vwv1); + int flags = SVAL(inbuf,smb_vwv2); + BOOL target_is_directory=False; + + *directory = *mask = 0; + + cnum = SVAL(inbuf,smb_tid); + + strcpy(name,smb_buf(inbuf)); + strcpy(newname,smb_buf(inbuf) + 1 + strlen(name)); + + DEBUG(3,("reply_copy : %s -> %s\n",name,newname)); + + if (tid2 != cnum) { + /* can't currently handle inter share copies XXXX */ + DEBUG(3,("Rejecting inter-share copy\n")); + return(ERROR(ERRSRV,ERRinvdevice)); + } + + unix_convert(name,cnum); + unix_convert(newname,cnum); + + target_is_directory = directory_exist(newname,NULL); + + if ((flags&1) && target_is_directory) { + return(ERROR(ERRDOS,ERRbadfile)); + } + + if ((flags&2) && !target_is_directory) { + return(ERROR(ERRDOS,ERRbadpath)); + } + + if ((flags&(1<<5)) && directory_exist(name,NULL)) { + /* wants a tree copy! XXXX */ + DEBUG(3,("Rejecting tree copy\n")); + return(ERROR(ERRSRV,ERRerror)); + } + + p = strrchr(name,'/'); + if (!p) { + strcpy(directory,"./"); + strcpy(mask,name); + } else { + *p = 0; + strcpy(directory,name); + strcpy(mask,p+1); + } + + if (is_mangled(mask)) + check_mangled_stack(mask); + + has_wild = strchr(mask,'*') || strchr(mask,'?'); + + if (!has_wild) { + strcat(directory,"/"); + strcat(directory,mask); + if (resolve_wildcards(directory,newname) && + copy_file(directory,newname,cnum,ofun, + count,target_is_directory)) count++; + if (!count) exists = file_exist(directory,NULL); + } else { + void *dirptr = NULL; + char *dname; + pstring destname; + + if (check_name(directory,cnum)) + dirptr = OpenDir(directory); + + if (dirptr) + { + error = ERRbadfile; + + if (strequal(mask,"????????.???")) + strcpy(mask,"*"); + + while ((dname = ReadDirName(dirptr))) + { + pstring fname; + strcpy(fname,dname); + + if(!mask_match(fname, mask, case_sensitive, False)) continue; + + error = ERRnoaccess; + sprintf(fname,"%s/%s",directory,dname); + strcpy(destname,newname); + if (resolve_wildcards(fname,destname) && + copy_file(directory,newname,cnum,ofun, + count,target_is_directory)) count++; + DEBUG(3,("reply_copy : doing copy on %s -> %s\n",fname,destname)); + } + CloseDir(dirptr); + } + } + + if (count == 0) { + if (exists) + return(ERROR(ERRDOS,error)); + else + return(UNIXERROR(ERRDOS,error)); + } + + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,count); + + return(outsize); +} + + + +/**************************************************************************** + reply to a setdir +****************************************************************************/ +int reply_setdir(char *inbuf,char *outbuf) +{ + int cnum,snum; + int outsize = 0; + BOOL ok = False; + pstring newdir; + + cnum = SVAL(inbuf,smb_tid); + + snum = Connections[cnum].service; + if (!CAN_SETDIR(snum)) + return(ERROR(ERRDOS,ERRnoaccess)); + + strcpy(newdir,smb_buf(inbuf) + 1); + strlower(newdir); + + if (strlen(newdir) == 0) + ok = True; + else + { + ok = directory_exist(newdir,NULL); + if (ok) + string_set(&Connections[cnum].connectpath,newdir); + } + + if (!ok) + return(ERROR(ERRDOS,ERRbadpath)); + + outsize = set_message(outbuf,0,0,True); + CVAL(outbuf,smb_reh) = CVAL(inbuf,smb_reh); + + DEBUG(3,("%s setdir %s cnum=%d\n",timestring(),newdir,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a lockingX request +****************************************************************************/ +int reply_lockingX(char *inbuf,char *outbuf,int length,int bufsize) +{ + int smb_com2 = CVAL(inbuf,smb_vwv0); + int smb_off2 = SVAL(inbuf,smb_vwv1); + int fnum = GETFNUM(inbuf,smb_vwv2); + uint16 locktype = SVAL(inbuf,smb_vwv3); + uint16 num_ulocks = SVAL(inbuf,smb_vwv6); + uint16 num_locks = SVAL(inbuf,smb_vwv7); + uint32 count, offset; + + int cnum; + int i; + char *data; + uint32 ecode=0, dummy2; + int outsize, eclass=0, dummy1; + + cnum = SVAL(inbuf,smb_tid); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + data = smb_buf(inbuf); + /* Data now points at the beginning of the list + of smb_unlkrng structs */ + for(i = 0; i < (int)num_ulocks; i++) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + if(!do_unlock(fnum,cnum,count,offset,&eclass, &ecode)) + return ERROR(eclass,ecode); + } + + /* Now do any requested locks */ + data += 10*num_ulocks; + /* Data now points at the beginning of the list + of smb_lkrng structs */ + for(i = 0; i < (int)num_locks; i++) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + if(!do_lock(fnum,cnum,count,offset, &eclass, &ecode)) + break; + } + + /* If any of the above locks failed, then we must unlock + all of the previous locks (X/Open spec). */ + if(i != num_locks && num_locks != 0) { + for(; i >= 0; i--) { + count = IVAL(data,SMB_LKLEN_OFFSET(i)); + offset = IVAL(data,SMB_LKOFF_OFFSET(i)); + do_unlock(fnum,cnum,count,offset,&dummy1,&dummy2); + } + return ERROR(eclass,ecode); + } + + outsize = set_message(outbuf,2,0,True); + + CVAL(outbuf,smb_vwv0) = smb_com2; + SSVAL(outbuf,smb_vwv1,(outsize+chain_size)-4); + + DEBUG(3,("%s lockingX fnum=%d cnum=%d type=%d num_locks=%d num_ulocks=%d\n", + timestring(),fnum,cnum,locktype,num_locks,num_ulocks)); + + chain_fnum = fnum; + + if (smb_com2 != 0xFF) + outsize += chain_reply(smb_com2,inbuf,inbuf+smb_off2+4, + outbuf,outbuf+outsize, + length,bufsize); + + chain_fnum = -1; + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBreadbmpx (read block multiplex) request +****************************************************************************/ +int reply_readbmpx(char *inbuf,char *outbuf,int length,int bufsize) +{ + int cnum,fnum; + int nread = -1; + int total_read; + char *data; + int32 startpos; + int outsize, mincount, maxcount; + int max_per_packet; + int tcount; + int pad; + + /* this function doesn't seem to work - disable by default */ + if (!lp_readbmpx()) + return(ERROR(ERRSRV,ERRuseSTD)); + + outsize = set_message(outbuf,8,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_READ(fnum); + CHECK_ERROR(fnum); + + startpos = IVAL(inbuf,smb_vwv1); + maxcount = SVAL(inbuf,smb_vwv3); + mincount = SVAL(inbuf,smb_vwv4); + + data = smb_buf(outbuf); + pad = ((int)data)%4; + if (pad) pad = 4 - pad; + data += pad; + + max_per_packet = bufsize-(outsize+pad); + tcount = maxcount; + total_read = 0; + + if (is_locked(fnum,cnum,maxcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + do + { + int N = MIN(max_per_packet,tcount-total_read); + + nread = read_file(fnum,data,startpos,N,N,-1,False); + + if (nread <= 0) nread = 0; + + if (nread < N) + tcount = total_read + nread; + + set_message(outbuf,8,nread,False); + SIVAL(outbuf,smb_vwv0,startpos); + SSVAL(outbuf,smb_vwv2,tcount); + SSVAL(outbuf,smb_vwv6,nread); + SSVAL(outbuf,smb_vwv7,smb_offset(data,outbuf)); + + send_smb(Client,outbuf); + + total_read += nread; + startpos += nread; + } + while (total_read < tcount); + + return(-1); +} + + +/**************************************************************************** + reply to a SMBwritebmpx (write block multiplex primary) request +****************************************************************************/ +int reply_writebmpx(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int32 startpos; + int tcount, write_through, smb_doff; + char *data; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + CHECK_ERROR(fnum); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv3); + write_through = BITSETW(inbuf+smb_vwv7,0); + numtowrite = SVAL(inbuf,smb_vwv10); + smb_doff = SVAL(inbuf,smb_vwv11); + + data = smb_base(inbuf) + smb_doff; + + /* If this fails we need to send an SMBwriteC response, + not an SMBwritebmpx - set this up now so we don't forget */ + CVAL(outbuf,smb_com) = SMBwritec; + + if (is_locked(fnum,cnum,tcount,startpos)) + return(ERROR(ERRDOS,ERRlock)); + + seek_file(fnum,startpos); + nwritten = write_file(fnum,data,numtowrite); + + if(lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if(nwritten < numtowrite) + return(UNIXERROR(ERRHRD,ERRdiskfull)); + + /* If the maximum to be written to this file + is greater than what we just wrote then set + up a secondary struct to be attached to this + fd, we will use this to cache error messages etc. */ + if(tcount > nwritten) + { + write_bmpx_struct *wbms; + if(Files[fnum].wbmpx_ptr != NULL) + wbms = Files[fnum].wbmpx_ptr; /* Use an existing struct */ + else + wbms = (write_bmpx_struct *)malloc(sizeof(write_bmpx_struct)); + if(!wbms) + { + DEBUG(0,("Out of memory in reply_readmpx\n")); + return(ERROR(ERRSRV,ERRnoresource)); + } + wbms->wr_mode = write_through; + wbms->wr_discard = False; /* No errors yet */ + wbms->wr_total_written = nwritten; + wbms->wr_errclass = 0; + wbms->wr_error = 0; + Files[fnum].wbmpx_ptr = wbms; + } + + /* We are returning successfully, set the message type back to + SMBwritebmpx */ + CVAL(outbuf,smb_com) = SMBwriteBmpx; + + outsize = set_message(outbuf,1,0,True); + + SSVALS(outbuf,smb_vwv0,-1); /* We don't support smb_remaining */ + + DEBUG(3,("%s writebmpx fnum=%d cnum=%d num=%d wrote=%d\n", + timestring(),fnum,cnum,numtowrite,nwritten)); + + if (write_through && tcount==nwritten) { + /* we need to send both a primary and a secondary response */ + smb_setlen(outbuf,outsize - 4); + send_smb(Client,outbuf); + + /* now the secondary */ + outsize = set_message(outbuf,1,0,True); + CVAL(outbuf,smb_com) = SMBwritec; + SSVAL(outbuf,smb_vwv0,nwritten); + } + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBwritebs (write block multiplex secondary) request +****************************************************************************/ +int reply_writebs(char *inbuf,char *outbuf) +{ + int cnum,numtowrite,fnum; + int nwritten = -1; + int outsize = 0; + int32 startpos; + int tcount, write_through, smb_doff; + char *data; + write_bmpx_struct *wbms; + BOOL send_response = False; + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + CHECK_FNUM(fnum,cnum); + CHECK_WRITE(fnum); + + tcount = SVAL(inbuf,smb_vwv1); + startpos = IVAL(inbuf,smb_vwv2); + numtowrite = SVAL(inbuf,smb_vwv6); + smb_doff = SVAL(inbuf,smb_vwv7); + + data = smb_base(inbuf) + smb_doff; + + /* We need to send an SMBwriteC response, not an SMBwritebs */ + CVAL(outbuf,smb_com) = SMBwritec; + + /* This fd should have an auxiliary struct attached, + check that it does */ + wbms = Files[fnum].wbmpx_ptr; + if(!wbms) return(-1); + + /* If write through is set we can return errors, else we must + cache them */ + write_through = wbms->wr_mode; + + /* Check for an earlier error */ + if(wbms->wr_discard) + return -1; /* Just discard the packet */ + + seek_file(fnum,startpos); + nwritten = write_file(fnum,data,numtowrite); + + if(lp_syncalways(SNUM(cnum)) || write_through) + sync_file(fnum); + + if (nwritten < numtowrite) + { + if(write_through) { + /* We are returning an error - we can delete the aux struct */ + if (wbms) free((char *)wbms); + Files[fnum].wbmpx_ptr = NULL; + return(ERROR(ERRHRD,ERRdiskfull)); + } + return(CACHE_ERROR(wbms,ERRHRD,ERRdiskfull)); + } + + /* Increment the total written, if this matches tcount + we can discard the auxiliary struct (hurrah !) and return a writeC */ + wbms->wr_total_written += nwritten; + if(wbms->wr_total_written >= tcount) + { + if (write_through) { + outsize = set_message(outbuf,1,0,True); + SSVAL(outbuf,smb_vwv0,wbms->wr_total_written); + send_response = True; + } + + free((char *)wbms); + Files[fnum].wbmpx_ptr = NULL; + } + + if(send_response) + return(outsize); + + return(-1); +} + + +/**************************************************************************** + reply to a SMBsetattrE +****************************************************************************/ +int reply_setattrE(char *inbuf,char *outbuf) +{ + int cnum,fnum; + struct utimbuf unix_times; + int outsize = 0; + + outsize = set_message(outbuf,0,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + /* Convert the DOS times into unix times. Ignore create + time as UNIX can't set this. + */ + unix_times.actime = make_unix_date2(inbuf+smb_vwv3); + unix_times.modtime = make_unix_date2(inbuf+smb_vwv5); + + /* Set the date on this file */ + if(sys_utime(Files[fnum].name, &unix_times)) + return(ERROR(ERRDOS,ERRnoaccess)); + + DEBUG(3,("%s reply_setattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBgetattrE +****************************************************************************/ +int reply_getattrE(char *inbuf,char *outbuf) +{ + int cnum,fnum; + struct stat sbuf; + int outsize = 0; + int mode; + + outsize = set_message(outbuf,11,0,True); + + cnum = SVAL(inbuf,smb_tid); + fnum = GETFNUM(inbuf,smb_vwv0); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + /* Do an fstat on this file */ + if(fstat(Files[fnum].fd, &sbuf)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + mode = dos_mode(cnum,Files[fnum].name,&sbuf); + + /* Convert the times into dos times. Set create + date to be last modify date as UNIX doesn't save + this */ + put_dos_date2(outbuf,smb_vwv0,sbuf.st_mtime); + put_dos_date2(outbuf,smb_vwv2,sbuf.st_atime); + put_dos_date2(outbuf,smb_vwv4,sbuf.st_mtime); + if (mode & aDIR) + { + SIVAL(outbuf,smb_vwv6,0); + SIVAL(outbuf,smb_vwv8,0); + } + else + { + SIVAL(outbuf,smb_vwv6,sbuf.st_size); + SIVAL(outbuf,smb_vwv8,ROUNDUP(sbuf.st_size,1024)); + } + SSVAL(outbuf,smb_vwv10, mode); + + DEBUG(3,("%s reply_getattrE fnum=%d cnum=%d\n",timestring(),fnum,cnum)); + + return(outsize); +} + + + + + diff --git a/source3/smbd/server.c b/source3/smbd/server.c new file mode 100644 index 0000000000..5d8facef33 --- /dev/null +++ b/source3/smbd/server.c @@ -0,0 +1,4300 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Main SMB server routines + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +#include "pcap.h" +#include "trans2.h" +#include "reply.h" + +pstring servicesf = CONFIGFILE; +pstring OriginalDir ="/"; +extern pstring debugf; +extern pstring sesssetup_user; + +char *InBuffer = NULL; +char *OutBuffer = NULL; +char *last_inbuf = NULL; + +int initial_uid = 0; +int initial_gid = 0; + +BOOL share_mode_pending = False; + +/* have I done a become_user? */ +static struct { + int cnum, uid; +} last_user; + +/* the last message the was processed */ +int last_message = -1; + +/* a useful macro to debug the last message processed */ +#define LAST_MESSAGE() smb_fn_name(last_message) + +extern pstring scope; +extern int DEBUGLEVEL; +extern int case_default; +extern BOOL case_sensitive; +extern BOOL case_preserve; +extern BOOL use_mangled_map; +extern BOOL short_case_preserve; +extern BOOL case_mangle; +extern time_t smb_last_time; + +extern pstring user_socket_options; + +connection_struct Connections[MAX_CONNECTIONS]; +files_struct Files[MAX_OPEN_FILES]; + +extern int Protocol; + +int maxxmit = BUFFER_SIZE; + +int chain_size = 0; + +/* a fnum to use when chaining */ +int chain_fnum = -1; + +/* number of open connections */ +static int num_connections_open = 0; + +extern fstring remote_machine; + + +/* these can be set by some functions to override the error codes */ +int unix_ERR_class=SUCCESS; +int unix_ERR_code=0; + + +extern int extra_time_offset; + +extern pstring myhostname; +extern struct in_addr myip; + + +static int find_free_connection(int hash); + +#ifdef SMB_PASSWD +extern void generate_next_challenge(char *challenge); +extern void set_challenge(char *challenge); +#endif + +/* for readability... */ +#define IS_DOS_READONLY(test_mode) (((test_mode) & aRONLY) != 0) +#define IS_DOS_DIR(test_mode) (((test_mode) & aDIR) != 0) +#define IS_DOS_ARCHIVE(test_mode) (((test_mode) & aARCH) != 0) +#define IS_DOS_SYSTEM(test_mode) (((test_mode) & aSYSTEM) != 0) +#define IS_DOS_HIDDEN(test_mode) (((test_mode) & aHIDDEN) != 0) + + + +/**************************************************************************** + change a dos mode to a unix mode + base permission for files: + everybody gets read bit set + dos readonly is represented in unix by removing everyone's write bit + dos archive is represented in unix by the user's execute bit + dos system is represented in unix by the group's execute bit + dos hidden is represented in unix by the other's execute bit + base permission for directories: + dos directory is represented in unix by unix's dir bit and the exec bit +****************************************************************************/ +mode_t unix_mode(int cnum,int dosmode) +{ + mode_t result = (S_IRUSR | S_IRGRP | S_IROTH); + + if ( !IS_DOS_READONLY(dosmode) ) + result |= (S_IWUSR | S_IWGRP | S_IWOTH); + + if (IS_DOS_DIR(dosmode)) + result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR); + + if (MAP_ARCHIVE(cnum) && IS_DOS_ARCHIVE(dosmode)) + result |= S_IXUSR; + + if (MAP_SYSTEM(cnum) && IS_DOS_SYSTEM(dosmode)) + result |= S_IXGRP; + + if (MAP_HIDDEN(cnum) && IS_DOS_HIDDEN(dosmode)) + result |= S_IXOTH; + + result &= CREATE_MODE(cnum); + return(result); +} + + +/**************************************************************************** + change a unix mode to a dos mode +****************************************************************************/ +int dos_mode(int cnum,char *path,struct stat *sbuf) +{ + int result = 0; + +#if OLD_DOS_MODE + if (!CAN_WRITE(cnum) || !((sbuf->st_mode & S_IWOTH) || + Connections[cnum].admin_user || + ((sbuf->st_mode & S_IWUSR) && + Connections[cnum].uid==sbuf->st_uid) || + ((sbuf->st_mode & S_IWGRP) && + in_group(sbuf->st_gid,Connections[cnum].gid, + Connections[cnum].ngroups, + Connections[cnum].igroups)))) + result |= aRONLY; +#else + if (CAN_WRITE(cnum) && !lp_alternate_permissions(SNUM(cnum))) { + if (!((sbuf->st_mode & S_IWOTH) || + Connections[cnum].admin_user || + ((sbuf->st_mode & S_IWUSR) && Connections[cnum].uid==sbuf->st_uid) || + ((sbuf->st_mode & S_IWGRP) && + in_group(sbuf->st_gid,Connections[cnum].gid, + Connections[cnum].ngroups,Connections[cnum].igroups)))) + result |= aRONLY; + } else { + if ((sbuf->st_mode & S_IWUSR) == 0) + result |= aRONLY; + } +#endif + + if ((sbuf->st_mode & S_IXUSR) != 0) + result |= aARCH; + + if (MAP_SYSTEM(cnum) && ((sbuf->st_mode & S_IXGRP) != 0)) + result |= aSYSTEM; + + if (MAP_HIDDEN(cnum) && ((sbuf->st_mode & S_IXOTH) != 0)) + result |= aHIDDEN; + + if (S_ISDIR(sbuf->st_mode)) + result = aDIR | (result & aRONLY); + +#if LINKS_READ_ONLY + if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode)) + result |= aRONLY; +#endif + + /* hide files with a name starting with a . */ + if (lp_hide_dot_files(SNUM(cnum))) + { + char *p = strrchr(path,'/'); + if (p) + p++; + else + p = path; + + if (p[0] == '.' && p[1] != '.' && p[1] != 0) + result |= aHIDDEN; + } + + return(result); +} + + +/******************************************************************* +chmod a file - but preserve some bits +********************************************************************/ +int dos_chmod(int cnum,char *fname,int dosmode,struct stat *st) +{ + struct stat st1; + int mask=0; + int tmp; + int unixmode; + + if (!st) { + st = &st1; + if (sys_stat(fname,st)) return(-1); + } + + if (S_ISDIR(st->st_mode)) dosmode |= aDIR; + + if (dos_mode(cnum,fname,st) == dosmode) return(0); + + unixmode = unix_mode(cnum,dosmode); + + /* preserve the s bits */ + mask |= (S_ISUID | S_ISGID); + + /* preserve the t bit */ +#ifdef S_ISVTX + mask |= S_ISVTX; +#endif + + /* possibly preserve the x bits */ + if (!MAP_ARCHIVE(cnum)) mask |= S_IXUSR; + if (!MAP_SYSTEM(cnum)) mask |= S_IXGRP; + if (!MAP_HIDDEN(cnum)) mask |= S_IXOTH; + + unixmode |= (st->st_mode & mask); + + /* if we previously had any r bits set then leave them alone */ + if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { + unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); + unixmode |= tmp; + } + + /* if we previously had any w bits set then leave them alone + if the new mode is not rdonly */ + if (!IS_DOS_READONLY(dosmode) && + (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) { + unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); + unixmode |= tmp; + } + + return(chmod(fname,unixmode)); +} + + +/**************************************************************************** +check if two filenames are equal + +this needs to be careful about whether we are case sensitive +****************************************************************************/ +static BOOL fname_equal(char *name1, char *name2) +{ + int l1 = strlen(name1); + int l2 = strlen(name2); + + /* handle filenames ending in a single dot */ + if (l1-l2 == 1 && name1[l1-1] == '.' && lp_strip_dot()) + { + BOOL ret; + name1[l1-1] = 0; + ret = fname_equal(name1,name2); + name1[l1-1] = '.'; + return(ret); + } + + if (l2-l1 == 1 && name2[l2-1] == '.' && lp_strip_dot()) + { + BOOL ret; + name2[l2-1] = 0; + ret = fname_equal(name1,name2); + name2[l2-1] = '.'; + return(ret); + } + + /* now normal filename handling */ + if (case_sensitive) + return(strcmp(name1,name2) == 0); + + return(strequal(name1,name2)); +} + + +/**************************************************************************** +mangle the 2nd name and check if it is then equal to the first name +****************************************************************************/ +static BOOL mangled_equal(char *name1, char *name2) +{ + pstring tmpname; + + if (is_8_3(name2)) + return(False); + + strcpy(tmpname,name2); + mangle_name_83(tmpname); + + return(strequal(name1,tmpname)); +} + + +/**************************************************************************** +scan a directory to find a filename, matching without case sensitivity + +If the name looks like a mangled name then try via the mangling functions +****************************************************************************/ +static BOOL scan_directory(char *path, char *name,int snum,BOOL docache) +{ + void *cur_dir; + char *dname; + BOOL mangled; + fstring name2; + + mangled = is_mangled(name); + + /* handle null paths */ + if (*path == 0) + path = "."; + + if (docache && (dname = DirCacheCheck(path,name,snum))) { + strcpy(name, dname); + return(True); + } + + if (mangled) + check_mangled_stack(name); + + /* open the directory */ + if (!(cur_dir = OpenDir(path))) + { + DEBUG(3,("scan dir didn't open dir [%s]\n",path)); + return(False); + } + + /* now scan for matching names */ + while ((dname = ReadDirName(cur_dir))) + { + if (*dname == '.' && + (strequal(dname,".") || strequal(dname,".."))) + continue; + + strcpy(name2,dname); + if (!name_map_mangle(name2,False,snum)) continue; + + if ((mangled && mangled_equal(name,name2)) + || fname_equal(name, name2)) + { + /* we've found the file, change it's name and return */ + if (docache) DirCacheAdd(path,name,dname,snum); + strcpy(name, dname); + CloseDir(cur_dir); + return(True); + } + } + + CloseDir(cur_dir); + return(False); +} + +/**************************************************************************** +This routine is called to convert names from the dos namespace to unix +namespace. It needs to handle any case conversions, mangling, format +changes etc. + +We assume that we have already done a chdir() to the right "root" directory +for this service. + +The function will return False if some part of the name except for the last +part cannot be resolved +****************************************************************************/ +BOOL unix_convert(char *name,int cnum) +{ + struct stat st; + char *start, *end; + pstring dirpath; + + *dirpath = 0; + + /* convert to basic unix format - removing \ chars and cleaning it up */ + unix_format(name); + unix_clean_name(name); + + if (!case_sensitive && + (!case_preserve || (is_8_3(name) && !short_case_preserve))) + strnorm(name); + + /* names must be relative to the root of the service - trim any leading /. + also trim trailing /'s */ + trim_string(name,"/","/"); + + /* check if it's a printer file */ + if (Connections[cnum].printer) + { + if ((! *name) || strchr(name,'/') || !is_8_3(name)) + { + fstring name2; + sprintf(name2,"%.6s.XXXXXX",remote_machine); + strcpy(name,(char *)mktemp(name2)); + } + return(True); + } + + /* stat the name - if it exists then we are all done! */ + if (sys_stat(name,&st) == 0) + return(True); + + DEBUG(5,("unix_convert(%s,%d)\n",name,cnum)); + + /* a special case - if we don't have any mangling chars and are case + sensitive then searching won't help */ + if (case_sensitive && !is_mangled(name) && + !lp_strip_dot() && !use_mangled_map) + return(False); + + /* now we need to recursively match the name against the real + directory structure */ + + start = name; + while (strncmp(start,"./",2) == 0) + start += 2; + + /* now match each part of the path name separately, trying the names + as is first, then trying to scan the directory for matching names */ + for (;start;start = (end?end+1:(char *)NULL)) + { + /* pinpoint the end of this section of the filename */ + end = strchr(start, '/'); + + /* chop the name at this point */ + if (end) *end = 0; + + /* check if the name exists up to this point */ + if (sys_stat(name, &st) == 0) + { + /* it exists. it must either be a directory or this must be + the last part of the path for it to be OK */ + if (end && !(st.st_mode & S_IFDIR)) + { + /* an intermediate part of the name isn't a directory */ + DEBUG(5,("Not a dir %s\n",start)); + *end = '/'; + return(False); + } + } + else + { + pstring rest; + + *rest = 0; + + /* remember the rest of the pathname so it can be restored + later */ + if (end) strcpy(rest,end+1); + + + /* try to find this part of the path in the directory */ + if (strchr(start,'?') || strchr(start,'*') || + !scan_directory(dirpath, start, SNUM(cnum), end?True:False)) + { + if (end) + { + /* an intermediate part of the name can't be found */ + DEBUG(5,("Intermediate not found %s\n",start)); + *end = '/'; + return(False); + } + + /* just the last part of the name doesn't exist */ + /* we may need to strupper() or strlower() it in case + this conversion is being used for file creation + purposes */ + /* if the filename is of mixed case then don't normalise it */ + if (!case_preserve && + (!strhasupper(start) || !strhaslower(start))) + strnorm(start); + + /* check on the mangled stack to see if we can recover the + base of the filename */ + if (is_mangled(start)) + check_mangled_stack(start); + + DEBUG(5,("New file %s\n",start)); + return(True); + } + + /* restore the rest of the string */ + if (end) + { + strcpy(start+strlen(start)+1,rest); + end = start + strlen(start); + } + } + + /* add to the dirpath that we have resolved so far */ + if (*dirpath) strcat(dirpath,"/"); + strcat(dirpath,start); + + /* restore the / that we wiped out earlier */ + if (end) *end = '/'; + } + + /* the name has been resolved */ + DEBUG(5,("conversion finished %s\n",name)); + return(True); +} + + + + +#ifdef QUOTAS +#ifdef LINUX +/**************************************************************************** +try to get the disk space from disk quotas (LINUX version) +****************************************************************************/ +/* +If you didn't make the symlink to the quota package, too bad :( +*/ +#include "quota/quotactl.c" +#include "quota/hasquota.c" +static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize) +{ + uid_t euser_id; + struct dqblk D; + struct stat S; + dev_t devno ; + struct mntent *mnt; + FILE *fp; + int found ; + int qcmd, fd ; + char *qfpathname; + + /* find the block device file */ + + if ( stat(path, &S) == -1 ) + return(False) ; + + devno = S.st_dev ; + + fp = setmntent(MOUNTED,"r"); + found = False ; + + while ((mnt = getmntent(fp)) != (struct mntent *) 0) { + if ( stat(mnt->mnt_dir,&S) == -1 ) + continue ; + if (S.st_dev == devno) { + found = True ; + break ; + } + } + endmntent(fp) ; + + if ( ! found ) + return(False) ; + + qcmd = QCMD(Q_GETQUOTA, USRQUOTA); + + if (hasmntopt(mnt, MNTOPT_NOAUTO) || hasmntopt(mnt, MNTOPT_NOQUOTA)) + return(False) ; + + if (!hasquota(mnt, USRQUOTA, &qfpathname)) + return(False) ; + + euser_id = geteuid(); + seteuid(0); + + if (quotactl(qcmd, mnt->mnt_fsname, euser_id, (caddr_t)&D) != 0) { + if ((fd = open(qfpathname, O_RDONLY)) < 0) { + seteuid(euser_id); + return(False); + } + lseek(fd, (long) dqoff(euser_id), L_SET); + switch (read(fd, &D, sizeof(struct dqblk))) { + case 0:/* EOF */ + memset((caddr_t)&D, 0, sizeof(struct dqblk)); + break; + case sizeof(struct dqblk): /* OK */ + break; + default: /* ERROR */ + close(fd); + seteuid(euser_id); + return(False); + } + } + seteuid(euser_id); + *bsize=1024; + + if (D.dqb_bsoftlimit==0) + return(False); + if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curinodes>D.dqb_isoftlimit)) + { + *dfree = 0; + *dsize = D.dqb_curblocks; + } + else { + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + *dsize = D.dqb_bsoftlimit; + } + return (True); +} +#else +#ifndef CRAY +/**************************************************************************** +try to get the disk space from disk quotas +****************************************************************************/ +static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize) +{ + uid_t user_id, euser_id; + int r; + char dev_disk[256]; + struct dqblk D; + struct stat S; + /* find the block device file */ + if ((stat(path, &S)<0) || + (devnm(S_IFBLK, S.st_dev, dev_disk, 256, 0)<0)) return (False); + + euser_id = geteuid(); + +#ifdef USE_SETRES + /* for HPUX, real uid must be same as euid to execute quotactl for euid */ + user_id = getuid(); + setresuid(euser_id,-1,-1); +#endif + r=quotactl(Q_GETQUOTA, dev_disk, euser_id, &D); + #ifdef USE_SETRES + if (setresuid(user_id,-1,-1)) + DEBUG(5,("Unable to reset uid to %d\n", user_id)); + #endif + /* Use softlimit to determine disk space, except when it has been exceeded */ + *bsize = 1024; + if (r) + { + if (errno == EDQUOT) + { + *dfree =0; + *dsize =D.dqb_curblocks; + return (True); + } + else return(False); + } + /* Use softlimit to determine disk space, except when it has been exceeded */ + if ((D.dqb_curblocks>D.dqb_bsoftlimit)||(D.dqb_curfiles>D.dqb_fsoftlimit)) + { + *dfree = 0; + *dsize = D.dqb_curblocks; + } + else { + *dfree = D.dqb_bsoftlimit - D.dqb_curblocks; + *dsize = D.dqb_bsoftlimit; + } + return (True); +} +#else +/**************************************************************************** +try to get the disk space from disk quotas (CRAY VERSION) +****************************************************************************/ +static BOOL disk_quotas(char *path, int *bsize, int *dfree, int *dsize) +{ + struct mntent *mnt; + FILE *fd; + struct stat sbuf; + dev_t devno ; + static dev_t devno_cached = 0 ; + static char name[MNTMAXSTR] ; + struct q_request request ; + struct qf_header header ; + static int quota_default = 0 ; + int found ; + + if ( stat(path,&sbuf) == -1 ) + return(False) ; + + devno = sbuf.st_dev ; + + if ( devno != devno_cached ) { + + devno_cached = devno ; + + if ((fd = setmntent(KMTAB)) == NULL) + return(False) ; + + found = False ; + + while ((mnt = getmntent(fd)) != NULL) { + + if ( stat(mnt->mnt_dir,&sbuf) == -1 ) + continue ; + + if (sbuf.st_dev == devno) { + + found = True ; + break ; + + } + + } + + strcpy(name,mnt->mnt_dir) ; + endmntent(fd) ; + + if ( ! found ) + return(False) ; + } + + request.qf_magic = QF_MAGIC ; + request.qf_entry.id = geteuid() ; + + if (quotactl(name, Q_GETQUOTA, &request) == -1) + return(False) ; + + if ( ! request.user ) + return(False) ; + + if ( request.qf_entry.user_q.f_quota == QFV_DEFAULT ) { + + if ( ! quota_default ) { + + if ( quotactl(name, Q_GETHEADER, &header) == -1 ) + return(False) ; + else + quota_default = header.user_h.def_fq ; + } + + *dfree = quota_default ; + + }else if ( request.qf_entry.user_q.f_quota == QFV_PREVENT ) { + + *dfree = 0 ; + + }else{ + + *dfree = request.qf_entry.user_q.f_quota ; + + } + + *dsize = request.qf_entry.user_q.f_use ; + + if ( *dfree ) + *dfree -= *dsize ; + + if ( *dfree < 0 ) + *dfree = 0 ; + + *bsize = 4096 ; /* Cray blocksize */ + + return(True) ; + +} +#endif /* CRAY */ +#endif /* LINUX */ +#endif /* QUOTAS */ + + +/**************************************************************************** +normalise for DOS usage +****************************************************************************/ +static void disk_norm(int *bsize,int *dfree,int *dsize) +{ + /* check if the disk is beyond the max disk size */ + int maxdisksize = lp_maxdisksize(); + if (maxdisksize) { + /* convert to blocks - and don't overflow */ + maxdisksize = ((maxdisksize*1024)/(*bsize))*1024; + if (*dsize > maxdisksize) *dsize = maxdisksize; + if (*dfree > maxdisksize) *dfree = maxdisksize-1; /* the -1 should stop + applications getting + div by 0 errors */ + } + + while (*dfree > WORDMAX || *dsize > WORDMAX || *bsize < 512) + { + *dfree /= 2; + *dsize /= 2; + *bsize *= 2; + if (*bsize > WORDMAX ) + { + *bsize = WORDMAX; + if (*dsize > WORDMAX) + *dsize = WORDMAX; + if (*dfree > WORDMAX) + *dfree = WORDMAX; + break; + } + } +} + +/**************************************************************************** + return number of 1K blocks available on a path and total number +****************************************************************************/ +int disk_free(char *path,int *bsize,int *dfree,int *dsize) +{ + char *df_command = lp_dfree_command(); +#ifndef NO_STATFS +#ifdef USE_STATVFS + struct statvfs fs; +#else +#ifdef ULTRIX + struct fs_data fs; +#else + struct statfs fs; +#endif +#endif +#endif + +#ifdef QUOTAS + if (disk_quotas(path, bsize, dfree, dsize)) + { + disk_norm(bsize,dfree,dsize); + return(((*bsize)/1024)*(*dfree)); + } +#endif + + + /* possibly use system() to get the result */ + if (df_command && *df_command) + { + int ret; + pstring syscmd; + pstring outfile; + + sprintf(outfile,"/tmp/dfree.smb.%d",(int)getpid()); + sprintf(syscmd,"%s %s",df_command,path); + standard_sub_basic(syscmd); + + ret = smbrun(syscmd,outfile); + DEBUG(3,("Running the command `%s' gave %d\n",syscmd,ret)); + + { + FILE *f = fopen(outfile,"r"); + *dsize = 0; + *dfree = 0; + *bsize = 1024; + if (f) + { + fscanf(f,"%d %d %d",dsize,dfree,bsize); + fclose(f); + } + else + DEBUG(0,("Can't open %s\n",outfile)); + } + + unlink(outfile); + disk_norm(bsize,dfree,dsize); + return(((*bsize)/1024)*(*dfree)); + } + +#ifdef NO_STATFS + DEBUG(1,("Warning - no statfs function\n")); + return(1); +#else +#ifdef STATFS4 + if (statfs(path,&fs,sizeof(fs),0) != 0) +#else +#ifdef USE_STATVFS + if (statvfs(path, &fs)) +#else +#ifdef STATFS3 + if (statfs(path,&fs,sizeof(fs)) == -1) +#else + if (statfs(path,&fs) == -1) +#endif /* STATFS3 */ +#endif /* USE_STATVFS */ +#endif /* STATFS4 */ + { + DEBUG(3,("dfree call failed code errno=%d\n",errno)); + *bsize = 1024; + *dfree = 1; + *dsize = 1; + return(((*bsize)/1024)*(*dfree)); + } + +#ifdef ULTRIX + *bsize = 1024; + *dfree = fs.fd_req.bfree; + *dsize = fs.fd_req.btot; +#else +#ifdef USE_STATVFS + *bsize = fs.f_frsize; +#else +#ifdef USE_F_FSIZE + /* eg: osf1 has f_fsize = fundamental filesystem block size, + f_bsize = optimal transfer block size (MX: 94-04-19) */ + *bsize = fs.f_fsize; +#else + *bsize = fs.f_bsize; +#endif /* STATFS3 */ +#endif /* USE_STATVFS */ + +#ifdef STATFS4 + *dfree = fs.f_bfree; +#else + *dfree = fs.f_bavail; +#endif /* STATFS4 */ + *dsize = fs.f_blocks; +#endif /* ULTRIX */ + +#if defined(SCO) || defined(ISC) || defined(MIPS) + *bsize = 512; +#endif + +/* handle rediculous bsize values - some OSes are broken */ +if ((*bsize) < 512 || (*bsize)>0xFFFF) *bsize = 1024; + + disk_norm(bsize,dfree,dsize); + + if (*bsize < 256) + *bsize = 512; + if ((*dsize)<1) + { + DEBUG(0,("dfree seems to be broken on your system\n")); + *dsize = 20*1024*1024/(*bsize); + *dfree = MAX(1,*dfree); + } + return(((*bsize)/1024)*(*dfree)); +#endif +} + + +/**************************************************************************** +wrap it to get filenames right +****************************************************************************/ +int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize) +{ + return(disk_free(dos_to_unix(path,False),bsize,dfree,dsize)); +} + + + +/**************************************************************************** +check a filename - possibly caling reducename + +This is called by every routine before it allows an operation on a filename. +It does any final confirmation necessary to ensure that the filename is +a valid one for the user to access. +****************************************************************************/ +BOOL check_name(char *name,int cnum) +{ + BOOL ret; + + errno = 0; + + ret = reduce_name(name,Connections[cnum].connectpath,lp_widelinks(SNUM(cnum))); + if (!ret) + DEBUG(5,("check_name on %s failed\n",name)); + + return(ret); +} + +/**************************************************************************** +check a filename - possibly caling reducename +****************************************************************************/ +static void check_for_pipe(char *fname) +{ + /* special case of pipe opens */ + char s[10]; + StrnCpy(s,fname,9); + strlower(s); + if (strstr(s,"pipe/")) + { + DEBUG(3,("Rejecting named pipe open for %s\n",fname)); + unix_ERR_class = ERRSRV; + unix_ERR_code = ERRaccess; + } +} + + +/**************************************************************************** +open a file +****************************************************************************/ +void open_file(int fnum,int cnum,char *fname1,int flags,int mode) +{ + pstring fname; + + Files[fnum].open = False; + Files[fnum].fd = -1; + errno = EPERM; + + strcpy(fname,fname1); + + /* check permissions */ + if ((flags != O_RDONLY) && !CAN_WRITE(cnum) && !Connections[cnum].printer) + { + DEBUG(3,("Permission denied opening %s\n",fname)); + check_for_pipe(fname); + return; + } + + /* this handles a bug in Win95 - it doesn't say to create the file when it + should */ + if (Connections[cnum].printer) + flags |= O_CREAT; + +/* + if (flags == O_WRONLY) + DEBUG(3,("Bug in client? Set O_WRONLY without O_CREAT\n")); +*/ + +#if UTIME_WORKAROUND + /* XXXX - is this OK?? */ + /* this works around a utime bug but can cause other problems */ + if ((flags & (O_WRONLY|O_RDWR)) && (flags & O_CREAT) && !(flags & O_APPEND)) + sys_unlink(fname); +#endif + + + Files[fnum].fd = sys_open(fname,flags,mode); + + if ((Files[fnum].fd>=0) && + Connections[cnum].printer && lp_minprintspace(SNUM(cnum))) { + pstring dname; + int dum1,dum2,dum3; + char *p; + strcpy(dname,fname); + p = strrchr(dname,'/'); + if (p) *p = 0; + if (sys_disk_free(dname,&dum1,&dum2,&dum3) < + lp_minprintspace(SNUM(cnum))) { + close(Files[fnum].fd); + Files[fnum].fd = -1; + sys_unlink(fname); + errno = ENOSPC; + return; + } + } + + + /* Fix for files ending in '.' */ + if((Files[fnum].fd == -1) && (errno == ENOENT) && + (strchr(fname,'.')==NULL)) + { + strcat(fname,"."); + Files[fnum].fd = sys_open(fname,flags,mode); + } + +#if (defined(ENAMETOOLONG) && defined(HAVE_PATHCONF)) + if ((Files[fnum].fd == -1) && (errno == ENAMETOOLONG)) + { + int max_len; + char *p = strrchr(fname, '/'); + + if (p == fname) /* name is "/xxx" */ + { + max_len = pathconf("/", _PC_NAME_MAX); + p++; + } + else if ((p == NULL) || (p == fname)) + { + p = fname; + max_len = pathconf(".", _PC_NAME_MAX); + } + else + { + *p = '\0'; + max_len = pathconf(fname, _PC_NAME_MAX); + *p = '/'; + p++; + } + if (strlen(p) > max_len) + { + char tmp = p[max_len]; + + p[max_len] = '\0'; + if ((Files[fnum].fd = sys_open(fname,flags,mode)) == -1) + p[max_len] = tmp; + } + } +#endif + + if (Files[fnum].fd < 0) + { + DEBUG(3,("Error opening file %s (%s) (flags=%d)\n", + fname,strerror(errno),flags)); + check_for_pipe(fname); + return; + } + + if (Files[fnum].fd >= 0) + { + struct stat st; + Connections[cnum].num_files_open++; + fstat(Files[fnum].fd,&st); + Files[fnum].mode = st.st_mode; + Files[fnum].open_time = time(NULL); + Files[fnum].size = 0; + Files[fnum].pos = -1; + Files[fnum].open = True; + Files[fnum].mmap_ptr = NULL; + Files[fnum].mmap_size = 0; + Files[fnum].can_lock = True; + Files[fnum].can_read = ((flags & O_WRONLY)==0); + Files[fnum].can_write = ((flags & (O_WRONLY|O_RDWR))!=0); + Files[fnum].share_mode = 0; + Files[fnum].share_pending = False; + Files[fnum].print_file = Connections[cnum].printer; + Files[fnum].modified = False; + Files[fnum].cnum = cnum; + string_set(&Files[fnum].name,fname); + Files[fnum].wbmpx_ptr = NULL; + + /* + * If the printer is marked as postscript output a leading + * file identifier to ensure the file is treated as a raw + * postscript file. + * This has a similar effect as CtrlD=0 in WIN.INI file. + * tim@fsg.com 09/06/94 + */ + if (Files[fnum].print_file && POSTSCRIPT(cnum) && + Files[fnum].can_write) + { + DEBUG(3,("Writing postscript line\n")); + write_file(fnum,"%!\n",3); + } + + DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n", + timestring(),Connections[cnum].user,fname, + BOOLSTR(Files[fnum].can_read),BOOLSTR(Files[fnum].can_write), + Connections[cnum].num_files_open,fnum)); + + } + +#if USE_MMAP + /* mmap it if read-only */ + if (!Files[fnum].can_write) + { + Files[fnum].mmap_size = file_size(fname); + Files[fnum].mmap_ptr = (char *)mmap(NULL,Files[fnum].mmap_size, + PROT_READ,MAP_SHARED,Files[fnum].fd,0); + + if (Files[fnum].mmap_ptr == (char *)-1 || !Files[fnum].mmap_ptr) + { + DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno))); + Files[fnum].mmap_ptr = NULL; + } + } +#endif +} + +/******************************************************************* +sync a file +********************************************************************/ +void sync_file(int fnum) +{ +#ifndef NO_FSYNC + fsync(Files[fnum].fd); +#endif +} + +/**************************************************************************** +run a file if it is a magic script +****************************************************************************/ +static void check_magic(int fnum,int cnum) +{ + if (!*lp_magicscript(SNUM(cnum))) + return; + + DEBUG(5,("checking magic for %s\n",Files[fnum].name)); + + { + char *p; + if (!(p = strrchr(Files[fnum].name,'/'))) + p = Files[fnum].name; + else + p++; + + if (!strequal(lp_magicscript(SNUM(cnum)),p)) + return; + } + + { + int ret; + pstring magic_output; + pstring fname; + strcpy(fname,Files[fnum].name); + + if (*lp_magicoutput(SNUM(cnum))) + strcpy(magic_output,lp_magicoutput(SNUM(cnum))); + else + sprintf(magic_output,"%s.out",fname); + + chmod(fname,0755); + ret = smbrun(fname,magic_output); + DEBUG(3,("Invoking magic command %s gave %d\n",fname,ret)); + unlink(fname); + } +} + + +/**************************************************************************** +close a file - possibly invalidating the read prediction +****************************************************************************/ +void close_file(int fnum) +{ + int cnum = Files[fnum].cnum; + invalidate_read_prediction(Files[fnum].fd); + Files[fnum].open = False; + Connections[cnum].num_files_open--; + if(Files[fnum].wbmpx_ptr) + { + free((char *)Files[fnum].wbmpx_ptr); + Files[fnum].wbmpx_ptr = NULL; + } + +#if USE_MMAP + if(Files[fnum].mmap_ptr) + { + munmap(Files[fnum].mmap_ptr,Files[fnum].mmap_size); + Files[fnum].mmap_ptr = NULL; + } +#endif + + if (lp_share_modes(SNUM(cnum))) + del_share_mode(fnum); + + if (Files[fnum].modified) { + struct stat st; + if (fstat(Files[fnum].fd,&st) == 0) { + int dosmode = dos_mode(cnum,Files[fnum].name,&st); + if (!IS_DOS_ARCHIVE(dosmode)) { + dos_chmod(cnum,Files[fnum].name,dosmode | aARCH,&st); + } + } + } + + close(Files[fnum].fd); + + /* NT uses smbclose to start a print - weird */ + if (Files[fnum].print_file) + print_file(fnum); + + /* check for magic scripts */ + check_magic(fnum,cnum); + + DEBUG(2,("%s %s closed file %s (numopen=%d)\n", + timestring(),Connections[cnum].user,Files[fnum].name, + Connections[cnum].num_files_open)); +} + +enum {AFAIL,AREAD,AWRITE,AALL}; + +/******************************************************************* +reproduce the share mode access table +********************************************************************/ +static int access_table(int new_deny,int old_deny,int old_mode, + int share_pid,char *fname) +{ + if (new_deny == DENY_ALL || old_deny == DENY_ALL) return(AFAIL); + + if (new_deny == DENY_DOS || old_deny == DENY_DOS) { + if (old_deny == new_deny && share_pid == getpid()) + return(AALL); + + if (old_mode == 0) return(AREAD); + + /* the new smbpub.zip spec says that if the file extension is + .com, .dll, .exe or .sym then allow the open. I will force + it to read-only as this seems sensible although the spec is + a little unclear on this. */ + if ((fname = strrchr(fname,'.'))) { + if (strequal(fname,".com") || + strequal(fname,".dll") || + strequal(fname,".exe") || + strequal(fname,".sym")) + return(AREAD); + } + + return(AFAIL); + } + + switch (new_deny) + { + case DENY_WRITE: + if (old_deny==DENY_WRITE && old_mode==0) return(AREAD); + if (old_deny==DENY_READ && old_mode==0) return(AWRITE); + if (old_deny==DENY_NONE && old_mode==0) return(AALL); + return(AFAIL); + case DENY_READ: + if (old_deny==DENY_WRITE && old_mode==1) return(AREAD); + if (old_deny==DENY_READ && old_mode==1) return(AWRITE); + if (old_deny==DENY_NONE && old_mode==1) return(AALL); + return(AFAIL); + case DENY_NONE: + if (old_deny==DENY_WRITE) return(AREAD); + if (old_deny==DENY_READ) return(AWRITE); + if (old_deny==DENY_NONE) return(AALL); + return(AFAIL); + } + return(AFAIL); +} + +/******************************************************************* +check if the share mode on a file allows it to be deleted or unlinked +return True if sharing doesn't prevent the operation +********************************************************************/ +BOOL check_file_sharing(int cnum,char *fname) +{ + int pid=0; + int share_mode = get_share_mode_byname(cnum,fname,&pid); + + if (!pid || !share_mode) return(True); + + if (share_mode == DENY_DOS) + return(pid == getpid()); + + /* XXXX exactly what share mode combinations should be allowed for + deleting/renaming? */ + return(False); +} + +/**************************************************************************** + C. Hoch 11/22/95 + Helper for open_file_shared. + Truncate a file after checking locking; close file if locked. + **************************************************************************/ +static void truncate_unless_locked(int fnum, int cnum) +{ + if (Files[fnum].can_write){ + if (is_locked(fnum,cnum,0x3FFFFFFF,0)){ + close_file(fnum); + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRlock; + } + else + ftruncate(Files[fnum].fd,0); + } +} + + +/**************************************************************************** +open a file with a share mode +****************************************************************************/ +void open_file_shared(int fnum,int cnum,char *fname,int share_mode,int ofun, + int mode,int *Access,int *action) +{ + int flags=0; + int flags2=0; + int deny_mode = (share_mode>>4)&7; + struct stat sbuf; + BOOL file_existed = file_exist(fname,&sbuf); + BOOL fcbopen = False; + int share_pid=0; + + Files[fnum].open = False; + Files[fnum].fd = -1; + + /* this is for OS/2 EAs - try and say we don't support them */ + if (strstr(fname,".+,;=[].")) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERROR_EAS_NOT_SUPPORTED; + return; + } + + if ((ofun & 0x3) == 0 && file_existed) { + errno = EEXIST; + return; + } + + if (ofun & 0x10) + flags2 |= O_CREAT; + if ((ofun & 0x3) == 2) + flags2 |= O_TRUNC; + + /* note that we ignore the append flag as + append does not mean the same thing under dos and unix */ + + switch (share_mode&0xF) + { + case 1: + flags = O_WRONLY; + break; + case 0xF: + fcbopen = True; + flags = O_RDWR; + break; + case 2: + flags = O_RDWR; + break; + default: + flags = O_RDONLY; + break; + } + + if (flags != O_RDONLY && file_existed && + (!CAN_WRITE(cnum) || IS_DOS_READONLY(dos_mode(cnum,fname,&sbuf)))) { + if (!fcbopen) { + errno = EACCES; + return; + } + flags = O_RDONLY; + } + + if (deny_mode > DENY_NONE && deny_mode!=DENY_FCB) { + DEBUG(2,("Invalid deny mode %d on file %s\n",deny_mode,fname)); + errno = EINVAL; + return; + } + + if (deny_mode == DENY_FCB) deny_mode = DENY_DOS; + + if (lp_share_modes(SNUM(cnum))) { + int old_share=0; + + if (file_existed) + old_share = get_share_mode(cnum,&sbuf,&share_pid); + + if (share_pid) { + /* someone else has a share lock on it, check to see + if we can too */ + int old_open_mode = old_share&0xF; + int old_deny_mode = (old_share>>4)&7; + + if (deny_mode > 4 || old_deny_mode > 4 || old_open_mode > 2) { + DEBUG(2,("Invalid share mode (%d,%d,%d) on file %s\n", + deny_mode,old_deny_mode,old_open_mode,fname)); + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + return; + } + + { + int access_allowed = access_table(deny_mode,old_deny_mode,old_open_mode, + share_pid,fname); + + if ((access_allowed == AFAIL) || + (access_allowed == AREAD && flags == O_WRONLY) || + (access_allowed == AWRITE && flags == O_RDONLY)) { + DEBUG(2,("Share violation on file (%d,%d,%d,%d,%s) = %d\n", + deny_mode,old_deny_mode,old_open_mode, + share_pid,fname, + access_allowed)); + errno = EACCES; + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + return; + } + + if (access_allowed == AREAD) + flags = O_RDONLY; + + if (access_allowed == AWRITE) + flags = O_WRONLY; + } + } + } + + DEBUG(4,("calling open_file with flags=0x%X flags2=0x%X mode=0%o\n", + flags,flags2,mode)); + + open_file(fnum,cnum,fname,flags|(flags2&~(O_TRUNC)),mode); + if (!Files[fnum].open && flags==O_RDWR && errno!=ENOENT && fcbopen) { + flags = O_RDONLY; + open_file(fnum,cnum,fname,flags,mode); + } + + if (Files[fnum].open) { + int open_mode=0; + switch (flags) { + case O_RDONLY: + open_mode = 0; + break; + case O_RDWR: + open_mode = 2; + break; + case O_WRONLY: + open_mode = 1; + break; + } + + Files[fnum].share_mode = (deny_mode<<4) | open_mode; + Files[fnum].share_pending = True; + + if (Access) { + (*Access) = open_mode; + } + + if (action) { + if (file_existed && !(flags2 & O_TRUNC)) *action = 1; + if (!file_existed) *action = 2; + if (file_existed && (flags2 & O_TRUNC)) *action = 3; + } + + if (!share_pid) + share_mode_pending = True; + + if ((flags2&O_TRUNC) && file_existed) + truncate_unless_locked(fnum,cnum); + } +} + + + +/******************************************************************* +check for files that we should now set our share modes on +********************************************************************/ +static void check_share_modes(void) +{ + int i; + for (i=0;i<MAX_OPEN_FILES;i++) + if(Files[i].open && Files[i].share_pending) { + if (lp_share_modes(SNUM(Files[i].cnum))) { + int pid=0; + get_share_mode_by_fnum(Files[i].cnum,i,&pid); + if (!pid) { + set_share_mode(i,Files[i].share_mode); + Files[i].share_pending = False; + } + } else { + Files[i].share_pending = False; + } + } +} + + +/**************************************************************************** +seek a file. Try to avoid the seek if possible +****************************************************************************/ +int seek_file(int fnum,int pos) +{ + int offset = 0; + if (Files[fnum].print_file && POSTSCRIPT(Files[fnum].cnum)) + offset = 3; + + Files[fnum].pos = lseek(Files[fnum].fd,pos+offset,SEEK_SET) - offset; + return(Files[fnum].pos); +} + +/**************************************************************************** +read from a file +****************************************************************************/ +int read_file(int fnum,char *data,int pos,int mincnt,int maxcnt,int timeout,BOOL exact) +{ + int ret=0; + + if (!Files[fnum].can_write) + { + ret = read_predict(Files[fnum].fd, + pos, + data, + NULL, + maxcnt); + + data += ret; + maxcnt -= ret; + mincnt = MAX(mincnt-ret,0); + pos += ret; + } + +#if USE_MMAP + if (Files[fnum].mmap_ptr) + { + int num = MIN(maxcnt,Files[fnum].mmap_size-pos); + if (num > 0) + { + memcpy(data,Files[fnum].mmap_ptr+pos,num); + data += num; + pos += num; + maxcnt -= num; + mincnt = MAX(mincnt-num,0); + ret += num; + } + } +#endif + + if (maxcnt <= 0) + return(ret); + + if (seek_file(fnum,pos) != pos) + { + DEBUG(3,("Failed to seek to %d\n",pos)); + return(ret); + } + + if (maxcnt > 0) + ret += read_with_timeout(Files[fnum].fd, + data, + mincnt, + maxcnt, + timeout, + exact); + + return(ret); +} + + +/**************************************************************************** +write to a file +****************************************************************************/ +int write_file(int fnum,char *data,int n) +{ + if (!Files[fnum].can_write) { + errno = EPERM; + return(0); + } + + Files[fnum].modified = True; + + return(write_data(Files[fnum].fd,data,n)); +} + + +static int old_umask = 022; + +/**************************************************************************** +load parameters specific to a connection/service +****************************************************************************/ +BOOL become_service(int cnum,BOOL do_chdir) +{ + extern char magic_char; + static int last_cnum = -1; + int snum; + + if (!OPEN_CNUM(cnum)) + { + last_cnum = -1; + return(False); + } + + Connections[cnum].lastused = smb_last_time; + + snum = SNUM(cnum); + + if (do_chdir && + ChDir(Connections[cnum].connectpath) != 0 && + ChDir(Connections[cnum].origpath) != 0) + { + DEBUG(0,("%s chdir (%s) failed cnum=%d\n",timestring(), + Connections[cnum].connectpath,cnum)); + return(False); + } + + if (cnum == last_cnum) + return(True); + + last_cnum = cnum; + + case_default = lp_defaultcase(snum); + case_preserve = lp_preservecase(snum); + short_case_preserve = lp_shortpreservecase(snum); + case_mangle = lp_casemangle(snum); + case_sensitive = lp_casesensitive(snum); + magic_char = lp_magicchar(snum); + use_mangled_map = (*lp_mangled_map(snum) ? True:False); + return(True); +} + + +/**************************************************************************** + become the specified uid +****************************************************************************/ +static BOOL become_uid(int uid) +{ + if (initial_uid != 0) + return(True); + +#ifdef AIX + { + /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */ + priv_t priv; + + priv.pv_priv[0] = 0; + priv.pv_priv[1] = 0; + if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH, + &priv, sizeof(priv_t)) < 0 || + setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 || + seteuid((uid_t)uid) < 0) + DEBUG(1,("Can't set uid (AIX3)")); + } +#endif + +#ifdef USE_SETRES + if (setresuid(-1,uid,-1) != 0) +#else + if ((seteuid(uid) != 0) && + (setuid(uid) != 0)) +#endif + { + DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n", + uid,getuid(), geteuid())); + if (uid > 32000) + DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n")); + return(False); + } + + if (((uid == -1) || (uid == 65535)) && geteuid() != uid) + { + DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n")); + return(False); + } + + return(True); +} + + +/**************************************************************************** + become the specified gid +****************************************************************************/ +static BOOL become_gid(int gid) +{ + if (initial_uid != 0) + return(True); + +#ifdef USE_SETRES + if (setresgid(-1,gid,-1) != 0) +#else + if (setgid(gid) != 0) +#endif + { + DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n", + gid,getgid(),getegid())); + if (gid > 32000) + DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n")); + return(False); + } + + return(True); +} + + +/**************************************************************************** + become the specified uid and gid +****************************************************************************/ +static BOOL become_id(int uid,int gid) +{ + return(become_gid(gid) && become_uid(uid)); +} + +/**************************************************************************** +become the guest user +****************************************************************************/ +static BOOL become_guest(void) +{ + BOOL ret; + static struct passwd *pass=NULL; + + if (initial_uid != 0) + return(True); + + if (!pass) + pass = Get_Pwnam(lp_guestaccount(-1),True); + if (!pass) return(False); + + ret = become_id(pass->pw_uid,pass->pw_gid); + + if (!ret) + DEBUG(1,("Failed to become guest. Invalid guest account?\n")); + + last_user.cnum = -2; + + return(ret); +} + +/******************************************************************* +check if a username is OK +********************************************************************/ +static BOOL check_user_ok(int cnum,user_struct *vuser,int snum) +{ + int i; + for (i=0;i<Connections[cnum].uid_cache.entries;i++) + if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True); + + if (!user_ok(vuser->name,snum)) return(False); + + i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE; + Connections[cnum].uid_cache.list[i] = vuser->uid; + + if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE) + Connections[cnum].uid_cache.entries++; + + return(True); +} + + +/**************************************************************************** + become the user of a connection number +****************************************************************************/ +BOOL become_user(int cnum, int uid) +{ + int new_umask; + user_struct *vuser; + int snum,gid; + int ngroups; + gid_t *groups; + + if (last_user.cnum == cnum && last_user.uid == uid) { + DEBUG(4,("Skipping become_user - already user\n")); + return(True); + } + + unbecome_user(); + + if (!OPEN_CNUM(cnum)) { + DEBUG(2,("Connection %d not open\n",cnum)); + return(False); + } + + snum = Connections[cnum].service; + + if (Connections[cnum].force_user || + lp_security() == SEC_SHARE || + !(vuser = get_valid_user_struct(uid)) || + !check_user_ok(cnum,vuser,snum)) { + uid = Connections[cnum].uid; + gid = Connections[cnum].gid; + groups = Connections[cnum].groups; + ngroups = Connections[cnum].ngroups; + } else { + if (!vuser) { + DEBUG(2,("Invalid vuid used %d\n",uid)); + return(False); + } + uid = vuser->uid; + if(!*lp_force_group(snum)) + gid = vuser->gid; + else + gid = Connections[cnum].gid; + groups = vuser->user_groups; + ngroups = vuser->user_ngroups; + } + + if (initial_uid == 0) + { + if (!become_gid(gid)) return(False); + +#ifndef NO_SETGROUPS + if (!IS_IPC(cnum)) { + /* groups stuff added by ih/wreu */ + if (ngroups > 0) + if (setgroups(ngroups,groups)<0) + DEBUG(0,("setgroups call failed!\n")); + } +#endif + + if (!Connections[cnum].admin_user && !become_uid(uid)) + return(False); + } + + new_umask = 0777 & ~CREATE_MODE(cnum); + old_umask = umask(new_umask); + + last_user.cnum = cnum; + last_user.uid = uid; + + DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d) new_umask=0%o\n", + getuid(),geteuid(),getgid(),getegid(),new_umask)); + + return(True); +} + +/**************************************************************************** + unbecome the user of a connection number +****************************************************************************/ +BOOL unbecome_user(void ) +{ + if (last_user.cnum == -1) + return(False); + + ChDir(OriginalDir); + + umask(old_umask); + + if (initial_uid == 0) + { +#ifdef USE_SETRES + setresuid(-1,getuid(),-1); + setresgid(-1,getgid(),-1); +#else + if (seteuid(initial_uid) != 0) + setuid(initial_uid); + setgid(initial_gid); +#endif + } +#ifdef NO_EID + if (initial_uid == 0) + DEBUG(2,("Running with no EID\n")); + initial_uid = getuid(); + initial_gid = getgid(); +#else + if (geteuid() != initial_uid) + { + DEBUG(0,("Warning: You appear to have a trapdoor uid system\n")); + initial_uid = geteuid(); + } + if (getegid() != initial_gid) + { + DEBUG(0,("Warning: You appear to have a trapdoor gid system\n")); + initial_gid = getegid(); + } +#endif + + if (ChDir(OriginalDir) != 0) + DEBUG(0,("%s chdir(%s) failed in unbecome_user\n", + timestring(),OriginalDir)); + + DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n", + getuid(),geteuid(),getgid(),getegid())); + + last_user.cnum = -1; + + return(True); +} + +/**************************************************************************** + find a service entry +****************************************************************************/ +int find_service(char *service) +{ + int iService; + + string_sub(service,"\\","/"); + + iService = lp_servicenumber(service); + + /* now handle the special case of a home directory */ + if (iService < 0) + { + char *phome_dir = get_home_dir(service); + DEBUG(3,("checking for home directory %s gave %s\n",service, + phome_dir?phome_dir:"(NULL)")); + if (phome_dir) + { + int iHomeService; + if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0) + { + lp_add_home(service,iHomeService,phome_dir); + iService = lp_servicenumber(service); + } + } + } + + /* If we still don't have a service, attempt to add it as a printer. */ + if (iService < 0) + { + int iPrinterService; + + if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) + { + char *pszTemp; + + DEBUG(3,("checking whether %s is a valid printer name...\n", service)); + pszTemp = PRINTCAP; + if ((pszTemp != NULL) && pcap_printername_ok(service, pszTemp)) + { + DEBUG(3,("%s is a valid printer name\n", service)); + DEBUG(3,("adding %s as a printer service\n", service)); + lp_add_printer(service,iPrinterService); + iService = lp_servicenumber(service); + if (iService < 0) + DEBUG(0,("failed to add %s as a printer service!\n", service)); + } + else + DEBUG(3,("%s is not a valid printer name\n", service)); + } + } + + /* just possibly it's a default service? */ + if (iService < 0) + { + char *defservice = lp_defaultservice(); + if (defservice && *defservice && !strequal(defservice,service)) { + iService = find_service(defservice); + if (iService >= 0) { + string_sub(service,"_","/"); + iService = lp_add_service(service,iService); + } + } + } + + if (iService >= 0) + if (!VALID_SNUM(iService)) + { + DEBUG(0,("Invalid snum %d for %s\n",iService,service)); + iService = -1; + } + + if (iService < 0) + DEBUG(3,("find_service() failed to find service %s\n", service)); + + return (iService); +} + + +/**************************************************************************** + create an error packet from a cached error. +****************************************************************************/ +int cached_error_packet(char *inbuf,char *outbuf,int fnum,int line) +{ + write_bmpx_struct *wbmpx = Files[fnum].wbmpx_ptr; + + int32 eclass = wbmpx->wr_errclass; + int32 err = wbmpx->wr_error; + + /* We can now delete the auxiliary struct */ + free((char *)wbmpx); + Files[fnum].wbmpx_ptr = NULL; + return error_packet(inbuf,outbuf,eclass,err,line); +} + + +struct +{ + int unixerror; + int smbclass; + int smbcode; +} unix_smb_errmap[] = +{ + {EPERM,ERRDOS,ERRnoaccess}, + {EACCES,ERRDOS,ERRnoaccess}, + {ENOENT,ERRDOS,ERRbadfile}, + {EIO,ERRHRD,ERRgeneral}, + {EBADF,ERRSRV,ERRsrverror}, + {EINVAL,ERRSRV,ERRsrverror}, + {EEXIST,ERRDOS,ERRfilexists}, + {ENFILE,ERRDOS,ERRnofids}, + {EMFILE,ERRDOS,ERRnofids}, + {ENOSPC,ERRHRD,ERRdiskfull}, +#ifdef EDQUOT + {EDQUOT,ERRHRD,ERRdiskfull}, +#endif +#ifdef ENOTEMPTY + {ENOTEMPTY,ERRDOS,ERRnoaccess}, +#endif +#ifdef EXDEV + {EXDEV,ERRDOS,ERRdiffdevice}, +#endif + {EROFS,ERRHRD,ERRnowrite}, + {0,0,0} +}; + + +/**************************************************************************** + create an error packet from errno +****************************************************************************/ +int unix_error_packet(char *inbuf,char *outbuf,int def_class,uint32 def_code,int line) +{ + int eclass=def_class; + int ecode=def_code; + int i=0; + + if (unix_ERR_class != SUCCESS) + { + eclass = unix_ERR_class; + ecode = unix_ERR_code; + unix_ERR_class = SUCCESS; + unix_ERR_code = 0; + } + else + { + while (unix_smb_errmap[i].smbclass != 0) + { + if (unix_smb_errmap[i].unixerror == errno) + { + eclass = unix_smb_errmap[i].smbclass; + ecode = unix_smb_errmap[i].smbcode; + break; + } + i++; + } + } + + return(error_packet(inbuf,outbuf,eclass,ecode,line)); +} + + +/**************************************************************************** + create an error packet. Normally called using the ERROR() macro +****************************************************************************/ +int error_packet(char *inbuf,char *outbuf,int error_class,uint32 error_code,int line) +{ + int outsize = set_message(outbuf,0,0,True); + int cmd; + cmd = CVAL(inbuf,smb_com); + + CVAL(outbuf,smb_rcls) = error_class; + SSVAL(outbuf,smb_err,error_code); + + DEBUG(3,("%s error packet at line %d cmd=%d (%s) eclass=%d ecode=%d\n", + timestring(), + line, + (int)CVAL(inbuf,smb_com), + smb_fn_name(CVAL(inbuf,smb_com)), + error_class, + error_code)); + + if (errno != 0) + DEBUG(3,("error string = %s\n",strerror(errno))); + + return(outsize); +} + + +#ifndef SIGCLD_IGNORE +/**************************************************************************** +this prevents zombie child processes +****************************************************************************/ +static int sig_cld() +{ + static int depth = 0; + if (depth != 0) + { + DEBUG(0,("ERROR: Recursion in sig_cld? Perhaps you need `#define USE_WAITPID'?\n")); + depth=0; + return(0); + } + depth++; + + BlockSignals(True); + DEBUG(5,("got SIGCLD\n")); + +#ifdef USE_WAITPID + while (waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0); +#endif + + /* Stop zombies */ + /* Stevens, Adv. Unix Prog. says that on system V you must call + wait before reinstalling the signal handler, because the kernel + calls the handler from within the signal-call when there is a + child that has exited. This would lead to an infinite recursion + if done vice versa. */ + +#ifndef DONT_REINSTALL_SIG +#ifdef SIGCLD_IGNORE + signal(SIGCLD, SIG_IGN); +#else + signal(SIGCLD, SIGNAL_CAST sig_cld); +#endif +#endif + +#ifndef USE_WAITPID + while (wait3(WAIT3_CAST1 NULL, WNOHANG, WAIT3_CAST2 NULL) > 0); +#endif + depth--; + BlockSignals(False); + return 0; +} +#endif + +/**************************************************************************** + this is called when the client exits abruptly + **************************************************************************/ +static int sig_pipe() +{ + exit_server("Got sigpipe\n"); + return(0); +} + +/**************************************************************************** + open the socket communication +****************************************************************************/ +static BOOL open_sockets(BOOL is_daemon,int port) +{ + extern int Client; + + if (is_daemon) + { + int s; + struct sockaddr addr; + int in_addrlen = sizeof(addr); + + /* Stop zombies */ +#ifdef SIGCLD_IGNORE + signal(SIGCLD, SIG_IGN); +#else + signal(SIGCLD, SIGNAL_CAST sig_cld); +#endif + + /* open an incoming socket */ + s = open_socket_in(SOCK_STREAM, port, 0); + if (s == -1) + return(False); + + /* ready to listen */ + if (listen(s, 5) == -1) + { + DEBUG(0,("listen: %s",strerror(errno))); + close(s); + return False; + } + + /* now accept incoming connections - forking a new process + for each incoming connection */ + DEBUG(2,("waiting for a connection\n")); + while (1) + { + Client = accept(s,&addr,&in_addrlen); + + if (Client == -1 && errno == EINTR) + continue; + + if (Client == -1) + { + DEBUG(0,("accept: %s",strerror(errno))); + return False; + } + +#ifdef NO_FORK_DEBUG +#ifndef NO_SIGNAL_TEST + signal(SIGPIPE, SIGNAL_CAST sig_pipe); + signal(SIGCLD, SIGNAL_CAST SIG_DFL); +#endif + return True; +#else + if (Client != -1 && fork()==0) + { +#ifndef NO_SIGNAL_TEST + signal(SIGPIPE, SIGNAL_CAST sig_pipe); + signal(SIGCLD, SIGNAL_CAST SIG_DFL); +#endif + /* close our standard file descriptors */ + close_low_fds(); + + set_socket_options(Client,"SO_KEEPALIVE"); + set_socket_options(Client,user_socket_options); + + return True; + } + close(Client); /* The parent doesn't need this socket */ +#endif + } + } + else + { + /* We will abort gracefully when the client or remote system + goes away */ +#ifndef NO_SIGNAL_TEST + signal(SIGPIPE, SIGNAL_CAST sig_pipe); +#endif + Client = dup(0); + + /* close our standard file descriptors */ + close_low_fds(); + + set_socket_options(Client,"SO_KEEPALIVE"); + set_socket_options(Client,user_socket_options); + } + + return True; +} + + +/**************************************************************************** +check if a snum is in use +****************************************************************************/ +BOOL snum_used(int snum) +{ + int i; + for (i=0;i<MAX_CONNECTIONS;i++) + if (OPEN_CNUM(i) && (SNUM(i) == snum)) + return(True); + return(False); +} + +/**************************************************************************** + reload the services file + **************************************************************************/ +BOOL reload_services(BOOL test) +{ + BOOL ret; + + if (lp_loaded()) + { + pstring fname; + strcpy(fname,lp_configfile()); + if (file_exist(fname,NULL) && !strcsequal(fname,servicesf)) + { + strcpy(servicesf,fname); + test = False; + } + } + + reopen_logs(); + + if (test && !lp_file_list_changed()) + return(True); + + lp_killunused(snum_used); + + ret = lp_load(servicesf,False); + + /* perhaps the config filename is now set */ + if (!test) + reload_services(True); + + reopen_logs(); + + { + extern int Client; + if (Client != -1) { + set_socket_options(Client,"SO_KEEPALIVE"); + set_socket_options(Client,user_socket_options); + } + } + + create_mangled_stack(lp_mangledstack()); + + /* this forces service parameters to be flushed */ + become_service(-1,True); + + return(ret); +} + + + +/**************************************************************************** +this prevents zombie child processes +****************************************************************************/ +static int sig_hup() +{ + BlockSignals(True); + DEBUG(0,("Got SIGHUP\n")); + reload_services(False); +#ifndef DONT_REINSTALL_SIG + signal(SIGHUP,SIGNAL_CAST sig_hup); +#endif + BlockSignals(False); + return(0); +} + +/**************************************************************************** +Setup the groups a user belongs to. +****************************************************************************/ +int setup_groups(char *user, int uid, int gid, int *p_ngroups, + int **p_igroups, gid_t **p_groups) +{ + if (-1 == initgroups(user,gid)) + { + if (getuid() == 0) + { + DEBUG(0,("Unable to initgroups!\n")); + if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000) + DEBUG(0,("This is probably a problem with the account %s\n",user)); + } + } + else + { + int i,ngroups; + int *igroups; + gid_t grp = 0; + ngroups = getgroups(0,&grp); + if (ngroups <= 0) + ngroups = 32; + igroups = (int *)malloc(sizeof(int)*ngroups); + for (i=0;i<ngroups;i++) + igroups[i] = 0x42424242; + ngroups = getgroups(ngroups,(gid_t *)igroups); + + if (igroups[0] == 0x42424242) + ngroups = 0; + + *p_ngroups = ngroups; + + /* The following bit of code is very strange. It is due to the + fact that some OSes use int* and some use gid_t* for + getgroups, and some (like SunOS) use both, one in prototypes, + and one in man pages and the actual code. Thus we detect it + dynamically using some very ugly code */ + if (ngroups > 0) + { + /* does getgroups return ints or gid_t ?? */ + static BOOL groups_use_ints = True; + + if (groups_use_ints && + ngroups == 1 && + SVAL(igroups,2) == 0x4242) + groups_use_ints = False; + + for (i=0;groups_use_ints && i<ngroups;i++) + if (igroups[i] == 0x42424242) + groups_use_ints = False; + + if (groups_use_ints) + { + *p_igroups = igroups; + *p_groups = (gid_t *)igroups; + } + else + { + gid_t *groups = (gid_t *)igroups; + igroups = (int *)malloc(sizeof(int)*ngroups); + for (i=0;i<ngroups;i++) + igroups[i] = groups[i]; + *p_igroups = igroups; + *p_groups = (gid_t *)groups; + } + } + DEBUG(3,("%s is in %d groups\n",user,ngroups)); + for (i=0;i<ngroups;i++) + DEBUG(3,("%d ",igroups[i])); + DEBUG(3,("\n")); + } + return 0; +} + +/**************************************************************************** + make a connection to a service +****************************************************************************/ +int make_connection(char *service,char *user,char *password, int pwlen, char *dev,int vuid) +{ + int cnum; + int snum; + struct passwd *pass = NULL; + connection_struct *pcon; + BOOL guest = False; + BOOL force = False; + static BOOL first_connection = True; + + strlower(service); + + snum = find_service(service); + if (snum < 0) + { + if (strequal(service,"IPC$")) + { + DEBUG(3,("%s refusing IPC connection\n",timestring())); + return(-3); + } + + DEBUG(0,("%s couldn't find service %s\n",timestring(),service)); + return(-2); + } + + if (strequal(service,HOMES_NAME)) + { + if (*user && Get_Pwnam(user,True)) + return(make_connection(user,user,password,pwlen,dev,vuid)); + + if (validated_username(vuid)) + { + strcpy(user,validated_username(vuid)); + return(make_connection(user,user,password,pwlen,dev,vuid)); + } + } + + if (!lp_snum_ok(snum) || !check_access(snum)) { + return(-4); + } + + /* you can only connect to the IPC$ service as an ipc device */ + if (strequal(service,"IPC$")) + strcpy(dev,"IPC"); + + if (*dev == '?' || !*dev) + { + if (lp_print_ok(snum)) + strcpy(dev,"LPT1:"); + else + strcpy(dev,"A:"); + } + + /* if the request is as a printer and you can't print then refuse */ + strupper(dev); + if (!lp_print_ok(snum) && (strncmp(dev,"LPT",3) == 0)) { + DEBUG(1,("Attempt to connect to non-printer as a printer\n")); + return(-6); + } + + /* lowercase the user name */ + strlower(user); + + /* add it as a possible user name */ + add_session_user(service); + + /* shall we let them in? */ + if (!authorise_login(snum,user,password,pwlen,&guest,&force,vuid)) + { + DEBUG(2,("%s invalid username/password for %s\n",timestring(),service)); + return(-1); + } + + cnum = find_free_connection(str_checksum(service) + str_checksum(user)); + if (cnum < 0) + { + DEBUG(0,("%s couldn't find free connection\n",timestring())); + return(-1); + } + + pcon = &Connections[cnum]; + bzero((char *)pcon,sizeof(*pcon)); + + /* find out some info about the user */ + pass = Get_Pwnam(user,True); + + if (pass == NULL) + { + DEBUG(0,("%s couldn't find account %s\n",timestring(),user)); + return(-7); + } + + pcon->read_only = lp_readonly(snum); + + { + pstring list; + StrnCpy(list,lp_readlist(snum),sizeof(pstring)-1); + string_sub(list,"%S",service); + + if (user_in_list(user,list)) + pcon->read_only = True; + + StrnCpy(list,lp_writelist(snum),sizeof(pstring)-1); + string_sub(list,"%S",service); + + if (user_in_list(user,list)) + pcon->read_only = False; + } + + /* admin user check */ + if (user_in_list(user,lp_admin_users(snum)) && + !pcon->read_only) + { + pcon->admin_user = True; + DEBUG(0,("%s logged in as admin user (root privileges)\n",user)); + } + else + pcon->admin_user = False; + + pcon->force_user = force; + pcon->uid = pass->pw_uid; + pcon->gid = pass->pw_gid; + pcon->num_files_open = 0; + pcon->lastused = time(NULL); + pcon->service = snum; + pcon->used = True; + pcon->printer = (strncmp(dev,"LPT",3) == 0); + pcon->ipc = (strncmp(dev,"IPC",3) == 0); + pcon->dirptr = NULL; + string_set(&pcon->dirpath,""); + string_set(&pcon->user,user); + +#if HAVE_GETGRNAM + if (*lp_force_group(snum)) + { + struct group *gptr = (struct group *)getgrnam(lp_force_group(snum)); + if (gptr) + { + pcon->gid = gptr->gr_gid; + DEBUG(3,("Forced group %s\n",lp_force_group(snum))); + } + else + DEBUG(1,("Couldn't find group %s\n",lp_force_group(snum))); + } +#endif + + if (*lp_force_user(snum)) + { + struct passwd *pass2; + fstring fuser; + strcpy(fuser,lp_force_user(snum)); + pass2 = (struct passwd *)Get_Pwnam(fuser,True); + if (pass2) + { + pcon->uid = pass2->pw_uid; + string_set(&pcon->user,fuser); + strcpy(user,fuser); + pcon->force_user = True; + DEBUG(3,("Forced user %s\n",fuser)); + } + else + DEBUG(1,("Couldn't find user %s\n",fuser)); + } + + { + pstring s; + strcpy(s,lp_pathname(snum)); + standard_sub(cnum,s); + string_set(&pcon->connectpath,s); + DEBUG(3,("Connect path is %s\n",s)); + } + + /* groups stuff added by ih */ + pcon->ngroups = 0; + pcon->groups = NULL; + + if (!IS_IPC(cnum)) + { + /* Find all the groups this uid is in and store them. Used by become_user() */ + setup_groups(pcon->user,pcon->uid,pcon->gid,&pcon->ngroups,&pcon->igroups,&pcon->groups); + + /* check number of connections */ + if (!claim_connection(cnum, + lp_servicename(SNUM(cnum)), + lp_max_connections(SNUM(cnum)),False)) + { + DEBUG(1,("too many connections - rejected\n")); + return(-8); + } + + if (lp_status(SNUM(cnum))) + claim_connection(cnum,"STATUS.",MAXSTATUS,first_connection); + + first_connection = False; + } /* IS_IPC */ + + pcon->open = True; + + /* execute any "root preexec = " line */ + if (*lp_rootpreexec(SNUM(cnum))) + { + pstring cmd; + strcpy(cmd,lp_rootpreexec(SNUM(cnum))); + standard_sub(cnum,cmd); + DEBUG(5,("cmd=%s\n",cmd)); + smbrun(cmd,NULL); + } + + if (!become_user(cnum,pcon->uid)) + { + DEBUG(0,("Can't become connected user!\n")); + pcon->open = False; + if (!IS_IPC(cnum)) { + yield_connection(cnum, + lp_servicename(SNUM(cnum)), + lp_max_connections(SNUM(cnum))); + if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS); + } + return(-1); + } + + if (ChDir(pcon->connectpath) != 0) + { + DEBUG(0,("Can't change directory to %s\n",pcon->connectpath)); + pcon->open = False; + unbecome_user(); + if (!IS_IPC(cnum)) { + yield_connection(cnum, + lp_servicename(SNUM(cnum)), + lp_max_connections(SNUM(cnum))); + if (lp_status(SNUM(cnum))) yield_connection(cnum,"STATUS.",MAXSTATUS); + } + return(-5); + } + + string_set(&pcon->origpath,pcon->connectpath); + +#if SOFTLINK_OPTIMISATION + /* resolve any soft links early */ + { + pstring s; + strcpy(s,pcon->connectpath); + GetWd(s); + string_set(&pcon->connectpath,s); + ChDir(pcon->connectpath); + } +#endif + + num_connections_open++; + add_session_user(user); + + /* execute any "preexec = " line */ + if (*lp_preexec(SNUM(cnum))) + { + pstring cmd; + strcpy(cmd,lp_preexec(SNUM(cnum))); + standard_sub(cnum,cmd); + smbrun(cmd,NULL); + } + + /* we've finished with the sensitive stuff */ + unbecome_user(); + + { + extern struct from_host Client_info; + DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) connect to service %s as user %s (uid=%d,gid=%d) (pid %d)\n", + timestring(), + Client_info.name,Client_info.addr, + lp_servicename(SNUM(cnum)),user, + pcon->uid, + pcon->gid, + (int)getpid())); + } + + return(cnum); +} + + +/**************************************************************************** + find first available file slot +****************************************************************************/ +int find_free_file(void ) +{ + int i; + for (i=1;i<MAX_OPEN_FILES;i++) + if (!Files[i].open) + return(i); + DEBUG(1,("ERROR! Out of file structures - perhaps increase MAX_OPEN_FILES?\n")); + return(-1); +} + +/**************************************************************************** + find first available connection slot, starting from a random position. +The randomisation stops problems with the server dieing and clients +thinking the server is still available. +****************************************************************************/ +static int find_free_connection(int hash ) +{ + int i; + BOOL used=False; + hash = (hash % (MAX_CONNECTIONS-2))+1; + + again: + + for (i=hash+1;i!=hash;) + { + if (!Connections[i].open && Connections[i].used == used) + { + DEBUG(3,("found free connection number %d\n",i)); + return(i); + } + i++; + if (i == MAX_CONNECTIONS) + i = 1; + } + + if (!used) + { + used = !used; + goto again; + } + + DEBUG(1,("ERROR! Out of connection structures\n")); + return(-1); +} + + +/**************************************************************************** +reply for the core protocol +****************************************************************************/ +int reply_corep(char *outbuf) +{ + int outsize = set_message(outbuf,1,0,True); + + Protocol = PROTOCOL_CORE; + + return outsize; +} + + +/**************************************************************************** +reply for the coreplus protocol +****************************************************************************/ +int reply_coreplus(char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int outsize = set_message(outbuf,13,0,True); + SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support + readbraw and writebraw (possibly) */ + CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */ + SSVAL(outbuf,smb_vwv1,0x1); /* user level security, don't encrypt */ + + Protocol = PROTOCOL_COREPLUS; + + return outsize; +} + + +/**************************************************************************** +reply for the lanman 1.0 protocol +****************************************************************************/ +int reply_lanman1(char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + BOOL doencrypt = SMBENCRYPT(); + time_t t = time(NULL); + + if (lp_security()>=SEC_USER) secword |= 1; + if (doencrypt) secword |= 2; + + set_message(outbuf,13,doencrypt?8:0,True); + SSVAL(outbuf,smb_vwv1,secword); +#ifdef SMB_PASSWD + /* Create a token value and add it to the outgoing packet. */ + if (doencrypt) + generate_next_challenge(smb_buf(outbuf)); +#endif + + Protocol = PROTOCOL_LANMAN1; + + if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) { + DEBUG(3,("using password server validation\n")); +#ifdef SMB_PASSWD + if (doencrypt) set_challenge(smb_buf(outbuf)); +#endif + } + + CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */ + SSVAL(outbuf,smb_vwv2,maxxmit); + SSVAL(outbuf,smb_vwv3,lp_maxmux()); /* maxmux */ + SSVAL(outbuf,smb_vwv4,1); + SSVAL(outbuf,smb_vwv5,raw); /* tell redirector we support + readbraw writebraw (possibly) */ + SIVAL(outbuf,smb_vwv6,getpid()); + SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60); + + put_dos_date(outbuf,smb_vwv8,t); + + return (smb_len(outbuf)+4); +} + + +/**************************************************************************** +reply for the lanman 2.0 protocol +****************************************************************************/ +int reply_lanman2(char *outbuf) +{ + int raw = (lp_readraw()?1:0) | (lp_writeraw()?2:0); + int secword=0; + BOOL doencrypt = SMBENCRYPT(); + time_t t = time(NULL); + + if (lp_security()>=SEC_USER) secword |= 1; + if (doencrypt) secword |= 2; + + set_message(outbuf,13,doencrypt?8:0,True); + SSVAL(outbuf,smb_vwv1,secword); +#ifdef SMB_PASSWD + /* Create a token value and add it to the outgoing packet. */ + if (doencrypt) + generate_next_challenge(smb_buf(outbuf)); +#endif + + SIVAL(outbuf,smb_vwv6,getpid()); + + Protocol = PROTOCOL_LANMAN2; + + if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) { + DEBUG(3,("using password server validation\n")); +#ifdef SMB_PASSWD + if (doencrypt) set_challenge(smb_buf(outbuf)); +#endif + } + + CVAL(outbuf,smb_flg) = 0x81; /* Reply, SMBlockread, SMBwritelock supported */ + SSVAL(outbuf,smb_vwv2,maxxmit); + SSVAL(outbuf,smb_vwv3,lp_maxmux()); + SSVAL(outbuf,smb_vwv4,1); + SSVAL(outbuf,smb_vwv5,raw); /* readbraw and/or writebraw */ + SSVAL(outbuf,smb_vwv10, TimeDiff(t)/60); + put_dos_date(outbuf,smb_vwv8,t); + + return (smb_len(outbuf)+4); +} + +/**************************************************************************** +reply for the nt protocol +****************************************************************************/ +int reply_nt1(char *outbuf) +{ + int capabilities=0x300; /* has dual names + lock_and_read */ + int secword=0; + BOOL doencrypt = SMBENCRYPT(); + + if (lp_security()>=SEC_USER) secword |= 1; + if (doencrypt) secword |= 2; + + set_message(outbuf,17,doencrypt?8:0,True); + CVAL(outbuf,smb_vwv1) = secword; +#ifdef SMB_PASSWD + /* Create a token value and add it to the outgoing packet. */ + if (doencrypt) { + generate_next_challenge(smb_buf(outbuf)); + /* Tell the nt machine how long the challenge is. */ + SSVALS(outbuf,smb_vwv16+1,8); + } +#endif + + SIVAL(outbuf,smb_vwv7+1,getpid()); /* session key */ + + Protocol = PROTOCOL_NT1; + + if (lp_security() == SEC_SERVER && server_cryptkey(outbuf)) { + DEBUG(3,("using password server validation\n")); +#ifdef SMB_PASSWD + if (doencrypt) set_challenge(smb_buf(outbuf)); +#endif + } + + if (lp_readraw() && lp_writeraw()) + capabilities |= 1; + + SSVAL(outbuf,smb_vwv1+1,lp_maxmux()); /* maxmpx */ + SSVAL(outbuf,smb_vwv2+1,1); /* num vcs */ + SIVAL(outbuf,smb_vwv3+1,0xFFFF); /* max buffer */ + SIVAL(outbuf,smb_vwv5+1,0xFFFF); /* raw size */ + SIVAL(outbuf,smb_vwv9+1,capabilities); /* capabilities */ + put_long_date(outbuf+smb_vwv11+1,time(NULL)); + SSVALS(outbuf,smb_vwv15+1,TimeDiff(time(NULL))/60); + + return (smb_len(outbuf)+4); +} + + +/* these are the protocol lists used for auto architecture detection: + +WinNT 3.51: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [MICROSOFT NETWORKS 1.03] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + +Win95: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [MICROSOFT NETWORKS 1.03] +protocol [LANMAN1.0] +protocol [Windows for Workgroups 3.1a] +protocol [LM1.2X002] +protocol [LANMAN2.1] +protocol [NT LM 0.12] + +OS/2: +protocol [PC NETWORK PROGRAM 1.0] +protocol [XENIX CORE] +protocol [LANMAN1.0] +protocol [LM1.2X002] +protocol [LANMAN2.1] +*/ + +/* + * Modified to recognize the architecture of the remote machine better. + * + * This appears to be the matrix of which protocol is used by which + * MS product. + Protocol WfWg Win95 WinNT OS/2 + PC NETWORK PROGRAM 1.0 1 1 1 1 + XENIX CORE 2 2 + MICROSOFT NETWORKS 3.0 2 2 + DOS LM1.2X002 3 3 + MICROSOFT NETWORKS 1.03 3 + DOS LANMAN2.1 4 4 + LANMAN1.0 4 3 + Windows for Workgroups 3.1a 5 5 5 + LM1.2X002 6 4 + LANMAN2.1 7 5 + NT LM 0.12 6 8 + * + * tim@fsg.com 09/29/95 + */ + +#define ARCH_WFWG 0x3 /* This is a fudge because WfWg is like Win95 */ +#define ARCH_WIN95 0x2 +#define ARCH_OS2 0xC /* Again OS/2 is like NT */ +#define ARCH_WINNT 0x8 +#define ARCH_SAMBA 0x10 + +#define ARCH_ALL 0x1F + +/* List of supported protocols, most desired first */ +struct { + char *proto_name; + char *short_name; + int (*proto_reply_fn)(char *); + int protocol_level; +} supported_protocols[] = { + {"NT LANMAN 1.0", "NT1", reply_nt1, PROTOCOL_NT1}, + {"NT LM 0.12", "NT1", reply_nt1, PROTOCOL_NT1}, + {"LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"Samba", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"DOS LM1.2X002", "LANMAN2", reply_lanman2, PROTOCOL_LANMAN2}, + {"LANMAN1.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, + {"MICROSOFT NETWORKS 3.0", "LANMAN1", reply_lanman1, PROTOCOL_LANMAN1}, + {"MICROSOFT NETWORKS 1.03", "COREPLUS", reply_coreplus, PROTOCOL_COREPLUS}, + {"PC NETWORK PROGRAM 1.0", "CORE", reply_corep, PROTOCOL_CORE}, + {NULL,NULL}, +}; + + +/**************************************************************************** + reply to a negprot +****************************************************************************/ +static int reply_negprot(char *inbuf,char *outbuf) +{ + extern fstring remote_arch; + int outsize = set_message(outbuf,1,0,True); + int Index=0; + int choice= -1; + int protocol; + char *p; + int bcc = SVAL(smb_buf(inbuf),-2); + int arch = ARCH_ALL; + + p = smb_buf(inbuf)+1; + while (p < (smb_buf(inbuf) + bcc)) + { + Index++; + DEBUG(3,("Requested protocol [%s]\n",p)); + if (strcsequal(p,"Windows for Workgroups 3.1a")) + arch &= ( ARCH_WFWG | ARCH_WIN95 | ARCH_WINNT ); + else if (strcsequal(p,"DOS LM1.2X002")) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcsequal(p,"DOS LANMAN2.1")) + arch &= ( ARCH_WFWG | ARCH_WIN95 ); + else if (strcsequal(p,"NT LM 0.12")) + arch &= ( ARCH_WIN95 | ARCH_WINNT ); + else if (strcsequal(p,"LANMAN2.1")) + arch &= ( ARCH_WINNT | ARCH_OS2 ); + else if (strcsequal(p,"LM1.2X002")) + arch &= ( ARCH_WINNT | ARCH_OS2 ); + else if (strcsequal(p,"MICROSOFT NETWORKS 1.03")) + arch &= ARCH_WINNT; + else if (strcsequal(p,"XENIX CORE")) + arch &= ( ARCH_WINNT | ARCH_OS2 ); + else if (strcsequal(p,"Samba")) { + arch = ARCH_SAMBA; + break; + } + + p += strlen(p) + 2; + } + + switch ( arch ) { + case ARCH_SAMBA: + strcpy(remote_arch,"Samba"); + break; + case ARCH_WFWG: + strcpy(remote_arch,"WfWg"); + break; + case ARCH_WIN95: + strcpy(remote_arch,"Win95"); + break; + case ARCH_WINNT: + strcpy(remote_arch,"WinNT"); + break; + case ARCH_OS2: + strcpy(remote_arch,"OS2"); + break; + default: + strcpy(remote_arch,"UNKNOWN"); + break; + } + + /* possibly reload - change of architecture */ + reload_services(True); + + /* a special case to stop password server loops */ + if (Index == 1 && strequal(remote_machine,myhostname) && + lp_security()==SEC_SERVER) + exit_server("Password server loop!"); + + /* Check for protocols, most desirable first */ + for (protocol = 0; supported_protocols[protocol].proto_name; protocol++) + { + p = smb_buf(inbuf)+1; + Index = 0; + if (lp_maxprotocol() >= supported_protocols[protocol].protocol_level) + while (p < (smb_buf(inbuf) + bcc)) + { + if (strequal(p,supported_protocols[protocol].proto_name)) + choice = Index; + Index++; + p += strlen(p) + 2; + } + if(choice != -1) + break; + } + + SSVAL(outbuf,smb_vwv0,choice); + if(choice != -1) { + extern fstring remote_proto; + strcpy(remote_proto,supported_protocols[protocol].short_name); + reload_services(True); + outsize = supported_protocols[protocol].proto_reply_fn(outbuf); + DEBUG(3,("Selected protocol %s\n",supported_protocols[protocol].proto_name)); + } + else { + DEBUG(0,("No protocol supported !\n")); + } + SSVAL(outbuf,smb_vwv0,choice); + + DEBUG(5,("%s negprot index=%d\n",timestring(),choice)); + + return(outsize); +} + + +/**************************************************************************** + parse a connect packet +****************************************************************************/ +void parse_connect(char *buf,char *service,char *user,char *password,int *pwlen,char *dev) +{ + char *p = smb_buf(buf) + 1; + char *p2; + + DEBUG(4,("parsing connect string %s\n",p)); + + p2 = strrchr(p,'\\'); + if (p2 == NULL) + strcpy(service,p); + else + strcpy(service,p2+1); + + p += strlen(p) + 2; + + strcpy(password,p); + *pwlen = strlen(password); + + p += strlen(p) + 2; + + strcpy(dev,p); + + *user = 0; + p = strchr(service,'%'); + if (p != NULL) + { + *p = 0; + strcpy(user,p+1); + } +} + + +/**************************************************************************** +close all open files for a connection +****************************************************************************/ +static void close_open_files(int cnum) +{ + int i; + for (i=0;i<MAX_OPEN_FILES;i++) + if( Files[i].cnum == cnum && Files[i].open) { + close_file(i); + } +} + + + +/**************************************************************************** +close a cnum +****************************************************************************/ +void close_cnum(int cnum, int uid) +{ + extern struct from_host Client_info; + + DirCacheFlush(SNUM(cnum)); + + unbecome_user(); + + if (!OPEN_CNUM(cnum)) + { + DEBUG(0,("Can't close cnum %d\n",cnum)); + return; + } + + DEBUG(IS_IPC(cnum)?3:1,("%s %s (%s) closed connection to service %s\n", + timestring(), + Client_info.name,Client_info.addr, + lp_servicename(SNUM(cnum)))); + + yield_connection(cnum, + lp_servicename(SNUM(cnum)), + lp_max_connections(SNUM(cnum))); + + if (lp_status(SNUM(cnum))) + yield_connection(cnum,"STATUS.",MAXSTATUS); + + close_open_files(cnum); + dptr_closecnum(cnum); + + /* execute any "postexec = " line */ + if (*lp_postexec(SNUM(cnum)) && become_user(cnum,uid)) + { + pstring cmd; + strcpy(cmd,lp_postexec(SNUM(cnum))); + standard_sub(cnum,cmd); + smbrun(cmd,NULL); + unbecome_user(); + } + + unbecome_user(); + /* execute any "root postexec = " line */ + if (*lp_rootpostexec(SNUM(cnum))) + { + pstring cmd; + strcpy(cmd,lp_rootpostexec(SNUM(cnum))); + standard_sub(cnum,cmd); + smbrun(cmd,NULL); + } + + Connections[cnum].open = False; + num_connections_open--; + if (Connections[cnum].ngroups && Connections[cnum].groups) + { + if (Connections[cnum].igroups != (int *)Connections[cnum].groups) + free(Connections[cnum].groups); + free(Connections[cnum].igroups); + Connections[cnum].groups = NULL; + Connections[cnum].igroups = NULL; + Connections[cnum].ngroups = 0; + } + + string_set(&Connections[cnum].user,""); + string_set(&Connections[cnum].dirpath,""); + string_set(&Connections[cnum].connectpath,""); +} + + +/**************************************************************************** +simple routines to do connection counting +****************************************************************************/ +BOOL yield_connection(int cnum,char *name,int max_connections) +{ + struct connect_record crec; + pstring fname; + FILE *f; + int mypid = getpid(); + int i; + + DEBUG(3,("Yielding connection to %d %s\n",cnum,name)); + + if (max_connections <= 0) + return(True); + + bzero(&crec,sizeof(crec)); + + strcpy(fname,lp_lockdir()); + standard_sub(cnum,fname); + trim_string(fname,"","/"); + + strcat(fname,"/"); + strcat(fname,name); + strcat(fname,".LCK"); + + f = fopen(fname,"r+"); + if (!f) + { + DEBUG(2,("Coudn't open lock file %s (%s)\n",fname,strerror(errno))); + return(False); + } + + fseek(f,0,SEEK_SET); + + /* find a free spot */ + for (i=0;i<max_connections;i++) + { + if (fread(&crec,sizeof(crec),1,f) != 1) + { + DEBUG(2,("Entry not found in lock file %s\n",fname)); + fclose(f); + return(False); + } + if (crec.pid == mypid && crec.cnum == cnum) + break; + } + + if (crec.pid != mypid || crec.cnum != cnum) + { + fclose(f); + DEBUG(2,("Entry not found in lock file %s\n",fname)); + return(False); + } + + bzero((void *)&crec,sizeof(crec)); + + /* remove our mark */ + if (fseek(f,i*sizeof(crec),SEEK_SET) != 0 || + fwrite(&crec,sizeof(crec),1,f) != 1) + { + DEBUG(2,("Couldn't update lock file %s (%s)\n",fname,strerror(errno))); + fclose(f); + return(False); + } + + DEBUG(3,("Yield successful\n")); + + fclose(f); + return(True); +} + + +/**************************************************************************** +simple routines to do connection counting +****************************************************************************/ +BOOL claim_connection(int cnum,char *name,int max_connections,BOOL Clear) +{ + struct connect_record crec; + pstring fname; + FILE *f; + int snum = SNUM(cnum); + int i,foundi= -1; + int total_recs; + + if (max_connections <= 0) + return(True); + + DEBUG(5,("trying claim %s %s %d\n",lp_lockdir(),name,max_connections)); + + strcpy(fname,lp_lockdir()); + standard_sub(cnum,fname); + trim_string(fname,"","/"); + + if (!directory_exist(fname,NULL)) + mkdir(fname,0755); + + strcat(fname,"/"); + strcat(fname,name); + strcat(fname,".LCK"); + + if (!file_exist(fname,NULL)) + { + f = fopen(fname,"w"); + if (f) fclose(f); + } + + total_recs = file_size(fname) / sizeof(crec); + + f = fopen(fname,"r+"); + + if (!f) + { + DEBUG(1,("couldn't open lock file %s\n",fname)); + return(False); + } + + /* find a free spot */ + for (i=0;i<max_connections;i++) + { + + if (i>=total_recs || + fseek(f,i*sizeof(crec),SEEK_SET) != 0 || + fread(&crec,sizeof(crec),1,f) != 1) + { + if (foundi < 0) foundi = i; + break; + } + + if (Clear && crec.pid && !process_exists(crec.pid)) + { + fseek(f,i*sizeof(crec),SEEK_SET); + bzero((void *)&crec,sizeof(crec)); + fwrite(&crec,sizeof(crec),1,f); + if (foundi < 0) foundi = i; + continue; + } + if (foundi < 0 && (!crec.pid || !process_exists(crec.pid))) + { + foundi=i; + if (!Clear) break; + } + } + + if (foundi < 0) + { + DEBUG(3,("no free locks in %s\n",fname)); + fclose(f); + return(False); + } + + /* fill in the crec */ + bzero((void *)&crec,sizeof(crec)); + crec.magic = 0x280267; + crec.pid = getpid(); + crec.cnum = cnum; + crec.uid = Connections[cnum].uid; + crec.gid = Connections[cnum].gid; + StrnCpy(crec.name,lp_servicename(snum),sizeof(crec.name)-1); + crec.start = time(NULL); + + { + extern struct from_host Client_info; + StrnCpy(crec.machine,Client_info.name,sizeof(crec.machine)-1); + StrnCpy(crec.addr,Client_info.addr,sizeof(crec.addr)-1); + } + + /* make our mark */ + if (fseek(f,foundi*sizeof(crec),SEEK_SET) != 0 || + fwrite(&crec,sizeof(crec),1,f) != 1) + { + fclose(f); + return(False); + } + + fclose(f); + return(True); +} + +#if DUMP_CORE +/******************************************************************* +prepare to dump a core file - carefully! +********************************************************************/ +static BOOL dump_core(void) +{ + char *p; + pstring dname; + strcpy(dname,debugf); + if ((p=strrchr(dname,'/'))) *p=0; + strcat(dname,"/corefiles"); + mkdir(dname,0700); + sys_chown(dname,getuid(),getgid()); + chmod(dname,0700); + if (chdir(dname)) return(False); + umask(~(0700)); + +#ifndef NO_GETRLIMIT +#ifdef RLIMIT_CORE + { + struct rlimit rlp; + getrlimit(RLIMIT_CORE, &rlp); + rlp.rlim_cur = MAX(4*1024*1024,rlp.rlim_cur); + setrlimit(RLIMIT_CORE, &rlp); + getrlimit(RLIMIT_CORE, &rlp); + DEBUG(3,("Core limits now %d %d\n",rlp.rlim_cur,rlp.rlim_max)); + } +#endif +#endif + + + DEBUG(0,("Dumping core in %s\n",dname)); + return(True); +} +#endif + +/**************************************************************************** +exit the server +****************************************************************************/ +void exit_server(char *reason) +{ + static int firsttime=1; + int i; + + if (!firsttime) exit(0); + firsttime = 0; + + unbecome_user(); + DEBUG(2,("Closing connections\n")); + for (i=0;i<MAX_CONNECTIONS;i++) + if (Connections[i].open) + close_cnum(i,-1); +#ifdef DFS_AUTH + if (dcelogin_atmost_once) + dfs_unlogin(); +#endif + if (!reason) { + int oldlevel = DEBUGLEVEL; + DEBUGLEVEL = 10; + DEBUG(0,("Last message was %s\n",smb_fn_name(last_message))); + if (last_inbuf) + show_msg(last_inbuf); + DEBUGLEVEL = oldlevel; + DEBUG(0,("===============================================================\n")); +#if DUMP_CORE + if (dump_core()) return; +#endif + } + DEBUG(3,("%s Server exit (%s)\n",timestring(),reason?reason:"")); + exit(0); +} + +/**************************************************************************** +do some standard substitutions in a string +****************************************************************************/ +void standard_sub(int cnum,char *s) +{ + if (!strchr(s,'%')) return; + + if (VALID_CNUM(cnum)) + { + string_sub(s,"%S",lp_servicename(Connections[cnum].service)); + string_sub(s,"%P",Connections[cnum].connectpath); + string_sub(s,"%u",Connections[cnum].user); + if (strstr(s,"%H")) { + char *home = get_home_dir(Connections[cnum].user); + if (home) string_sub(s,"%H",home); + } + string_sub(s,"%g",gidtoname(Connections[cnum].gid)); + } + standard_sub_basic(s); +} + +/* +These flags determine some of the permissions required to do an operation + +Note that I don't set NEED_WRITE on some write operations because they +are used by some brain-dead clients when printing, and I don't want to +force write permissions on print services. +*/ +#define AS_USER (1<<0) +#define NEED_WRITE (1<<1) +#define TIME_INIT (1<<2) +#define CAN_IPC (1<<3) +#define AS_GUEST (1<<5) + + +/* + define a list of possible SMB messages and their corresponding + functions. Any message that has a NULL function is unimplemented - + please feel free to contribute implementations! +*/ +struct smb_message_struct +{ + int code; + char *name; + int (*fn)(); + int flags; +#if PROFILING + unsigned long time; +#endif +} + smb_messages[] = { + + /* CORE PROTOCOL */ + + {SMBnegprot,"SMBnegprot",reply_negprot,0}, + {SMBtcon,"SMBtcon",reply_tcon,0}, + {SMBtdis,"SMBtdis",reply_tdis,0}, + {SMBexit,"SMBexit",reply_exit,0}, + {SMBioctl,"SMBioctl",reply_ioctl,0}, + {SMBecho,"SMBecho",reply_echo,0}, + {SMBsesssetupX,"SMBsesssetupX",reply_sesssetup_and_X,0}, + {SMBtconX,"SMBtconX",reply_tcon_and_X,0}, + {SMBulogoffX, "SMBulogoffX", reply_ulogoffX, 0}, + {SMBgetatr,"SMBgetatr",reply_getatr,AS_USER}, + {SMBsetatr,"SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, + {SMBchkpth,"SMBchkpth",reply_chkpth,AS_USER}, + {SMBsearch,"SMBsearch",reply_search,AS_USER}, + {SMBopen,"SMBopen",reply_open,AS_USER}, + + /* note that SMBmknew and SMBcreate are deliberately overloaded */ + {SMBcreate,"SMBcreate",reply_mknew,AS_USER}, + {SMBmknew,"SMBmknew",reply_mknew,AS_USER}, + + {SMBunlink,"SMBunlink",reply_unlink,AS_USER | NEED_WRITE}, + {SMBread,"SMBread",reply_read,AS_USER}, + {SMBwrite,"SMBwrite",reply_write,AS_USER}, + {SMBclose,"SMBclose",reply_close,AS_USER}, + {SMBmkdir,"SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, + {SMBrmdir,"SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, + {SMBdskattr,"SMBdskattr",reply_dskattr,AS_USER}, + {SMBmv,"SMBmv",reply_mv,AS_USER | NEED_WRITE}, + + /* this is a Pathworks specific call, allowing the + changing of the root path */ + {pSETDIR,"pSETDIR",reply_setdir,AS_USER}, + + {SMBlseek,"SMBlseek",reply_lseek,AS_USER}, + {SMBflush,"SMBflush",reply_flush,AS_USER}, + {SMBctemp,"SMBctemp",reply_ctemp,AS_USER}, + {SMBsplopen,"SMBsplopen",reply_printopen,AS_USER}, + {SMBsplclose,"SMBsplclose",reply_printclose,AS_USER}, + {SMBsplretq,"SMBsplretq",reply_printqueue,AS_USER}, + {SMBsplwr,"SMBsplwr",reply_printwrite,AS_USER}, + {SMBlock,"SMBlock",reply_lock,AS_USER}, + {SMBunlock,"SMBunlock",reply_unlock,AS_USER}, + + /* CORE+ PROTOCOL FOLLOWS */ + + {SMBreadbraw,"SMBreadbraw",reply_readbraw,AS_USER}, + {SMBwritebraw,"SMBwritebraw",reply_writebraw,AS_USER}, + {SMBwriteclose,"SMBwriteclose",reply_writeclose,AS_USER}, + {SMBlockread,"SMBlockread",reply_lockread,AS_USER}, + {SMBwriteunlock,"SMBwriteunlock",reply_writeunlock,AS_USER}, + + /* LANMAN1.0 PROTOCOL FOLLOWS */ + + {SMBreadBmpx,"SMBreadBmpx",reply_readbmpx,AS_USER}, + {SMBreadBs,"SMBreadBs",NULL,AS_USER}, + {SMBwriteBmpx,"SMBwriteBmpx",reply_writebmpx,AS_USER}, + {SMBwriteBs,"SMBwriteBs",reply_writebs,AS_USER}, + {SMBwritec,"SMBwritec",NULL,AS_USER}, + {SMBsetattrE,"SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE}, + {SMBgetattrE,"SMBgetattrE",reply_getattrE,AS_USER}, + {SMBtrans,"SMBtrans",reply_trans,AS_USER | CAN_IPC}, + {SMBtranss,"SMBtranss",NULL,AS_USER | CAN_IPC}, + {SMBioctls,"SMBioctls",NULL,AS_USER}, + {SMBcopy,"SMBcopy",reply_copy,AS_USER | NEED_WRITE}, + {SMBmove,"SMBmove",NULL,AS_USER | NEED_WRITE}, + + {SMBopenX,"SMBopenX",reply_open_and_X,AS_USER}, + {SMBreadX,"SMBreadX",reply_read_and_X,AS_USER}, + {SMBwriteX,"SMBwriteX",reply_write_and_X,AS_USER}, + {SMBlockingX,"SMBlockingX",reply_lockingX,AS_USER}, + + {SMBffirst,"SMBffirst",reply_search,AS_USER}, + {SMBfunique,"SMBfunique",reply_search,AS_USER}, + {SMBfclose,"SMBfclose",reply_fclose,AS_USER}, + + /* LANMAN2.0 PROTOCOL FOLLOWS */ + {SMBfindnclose, "SMBfindnclose", reply_findnclose, AS_USER}, + {SMBfindclose, "SMBfindclose", reply_findclose,AS_USER}, + {SMBtrans2, "SMBtrans2", reply_trans2, AS_USER}, + {SMBtranss2, "SMBtranss2", reply_transs2, AS_USER}, + + /* messaging routines */ + {SMBsends,"SMBsends",reply_sends,AS_GUEST}, + {SMBsendstrt,"SMBsendstrt",reply_sendstrt,AS_GUEST}, + {SMBsendend,"SMBsendend",reply_sendend,AS_GUEST}, + {SMBsendtxt,"SMBsendtxt",reply_sendtxt,AS_GUEST}, + + /* NON-IMPLEMENTED PARTS OF THE CORE PROTOCOL */ + + {SMBsendb,"SMBsendb",NULL,AS_GUEST}, + {SMBfwdname,"SMBfwdname",NULL,AS_GUEST}, + {SMBcancelf,"SMBcancelf",NULL,AS_GUEST}, + {SMBgetmac,"SMBgetmac",NULL,AS_GUEST} + }; + +/**************************************************************************** +return a string containing the function name of a SMB command +****************************************************************************/ +char *smb_fn_name(int type) +{ + static char *unknown_name = "SMBunknown"; + static int num_smb_messages = + sizeof(smb_messages) / sizeof(struct smb_message_struct); + int match; + + for (match=0;match<num_smb_messages;match++) + if (smb_messages[match].code == type) + break; + + if (match == num_smb_messages) + return(unknown_name); + + return(smb_messages[match].name); +} + + +/**************************************************************************** +do a switch on the message type, and return the response size +****************************************************************************/ +static int switch_message(int type,char *inbuf,char *outbuf,int size,int bufsize) +{ + static int pid= -1; + int outsize = 0; + static int num_smb_messages = + sizeof(smb_messages) / sizeof(struct smb_message_struct); + int match; + +#if PROFILING + struct timeval msg_start_time; + struct timeval msg_end_time; + static unsigned long total_time = 0; + + GetTimeOfDay(&msg_start_time); +#endif + + if (pid == -1) + pid = getpid(); + + errno = 0; + last_message = type; + + /* make sure this is an SMB packet */ + if (strncmp(smb_base(inbuf),"\377SMB",4) != 0) + { + DEBUG(2,("Non-SMB packet of length %d\n",smb_len(inbuf))); + return(-1); + } + + for (match=0;match<num_smb_messages;match++) + if (smb_messages[match].code == type) + break; + + if (match == num_smb_messages) + { + DEBUG(0,("Unknown message type %d!\n",type)); + outsize = reply_unknown(inbuf,outbuf); + } + else + { + DEBUG(3,("switch message %s (pid %d)\n",smb_messages[match].name,pid)); + if (smb_messages[match].fn) + { + int cnum = SVAL(inbuf,smb_tid); + int flags = smb_messages[match].flags; + int uid = SVAL(inbuf,smb_uid); + + /* does this protocol need to be run as root? */ + if (!(flags & AS_USER)) + unbecome_user(); + + /* does this protocol need to be run as the connected user? */ + if ((flags & AS_USER) && !become_user(cnum,uid)) + return(ERROR(ERRSRV,ERRinvnid)); + + /* does it need write permission? */ + if ((flags & NEED_WRITE) && !CAN_WRITE(cnum)) + return(ERROR(ERRSRV,ERRaccess)); + + /* ipc services are limited */ + if (IS_IPC(cnum) && (flags & AS_USER) && !(flags & CAN_IPC)) + return(ERROR(ERRSRV,ERRaccess)); + + /* load service specific parameters */ + if (OPEN_CNUM(cnum) && !become_service(cnum,(flags & AS_USER)?True:False)) + return(ERROR(ERRSRV,ERRaccess)); + + /* does this protocol need to be run as guest? */ + if ((flags & AS_GUEST) && (!become_guest() || !check_access(-1))) + return(ERROR(ERRSRV,ERRaccess)); + + last_inbuf = inbuf; + + outsize = smb_messages[match].fn(inbuf,outbuf,size,bufsize); + } + else + { + outsize = reply_unknown(inbuf,outbuf); + } + } + +#if PROFILING + GetTimeOfDay(&msg_end_time); + if (!(smb_messages[match].flags & TIME_INIT)) + { + smb_messages[match].time = 0; + smb_messages[match].flags |= TIME_INIT; + } + { + unsigned long this_time = + (msg_end_time.tv_sec - msg_start_time.tv_sec)*1e6 + + (msg_end_time.tv_usec - msg_start_time.tv_usec); + smb_messages[match].time += this_time; + total_time += this_time; + } + DEBUG(2,("TIME %s %d usecs %g pct\n", + smb_fn_name(type),smb_messages[match].time, + (100.0*smb_messages[match].time) / total_time)); +#endif + + return(outsize); +} + + +/**************************************************************************** +construct a chained reply and add it to the already made reply + +inbuf points to the original message start. +inbuf2 points to the smb_wct part of the secondary message +type is the type of the secondary message +outbuf points to the original outbuffer +outbuf2 points to the smb_wct field of the new outbuffer +size is the total length of the incoming message (from inbuf1) +bufsize is the total buffer size + +return how many bytes were added to the response +****************************************************************************/ +int chain_reply(int type,char *inbuf,char *inbuf2,char *outbuf,char *outbuf2,int size,int bufsize) +{ + int outsize = 0; + char *ibuf,*obuf; + static BOOL in_chain = False; + static char *last_outbuf=NULL; + BOOL was_inchain = in_chain; + int insize_remaining; + static int insize_deleted; + + + chain_size += PTR_DIFF(outbuf2,outbuf) - smb_wct; + if (was_inchain) + outbuf = last_outbuf; + else + insize_deleted = 0; + + + insize_deleted = 0; + inbuf2 -= insize_deleted; + insize_remaining = size - PTR_DIFF(inbuf2,inbuf); + insize_deleted += size - (insize_remaining + smb_wct); + + in_chain = True; + last_outbuf = outbuf; + + + /* allocate some space for the in and out buffers of the chained message */ + ibuf = (char *)malloc(size + SAFETY_MARGIN); + obuf = (char *)malloc(bufsize + SAFETY_MARGIN); + + if (!ibuf || !obuf) + { + DEBUG(0,("Out of memory in chain reply\n")); + return(ERROR(ERRSRV,ERRnoresource)); + } + + ibuf += SMB_ALIGNMENT; + obuf += SMB_ALIGNMENT; + + /* create the in buffer */ + memcpy(ibuf,inbuf,smb_wct); + memcpy(ibuf+smb_wct,inbuf2,insize_remaining); + CVAL(ibuf,smb_com) = type; + + /* create the out buffer */ + bzero(obuf,smb_size); + + set_message(obuf,0,0,True); + CVAL(obuf,smb_com) = CVAL(ibuf,smb_com); + + memcpy(obuf+4,ibuf+4,4); + CVAL(obuf,smb_rcls) = SUCCESS; + CVAL(obuf,smb_reh) = 0; + CVAL(obuf,smb_flg) = 0x80 | (CVAL(ibuf,smb_flg) & 0x8); /* bit 7 set + means a reply */ + SSVAL(obuf,smb_flg2,1); /* say we support long filenames */ + SSVAL(obuf,smb_err,SUCCESS); + SSVAL(obuf,smb_tid,SVAL(inbuf,smb_tid)); + SSVAL(obuf,smb_pid,SVAL(inbuf,smb_pid)); + SSVAL(obuf,smb_uid,SVAL(inbuf,smb_uid)); + SSVAL(obuf,smb_mid,SVAL(inbuf,smb_mid)); + + DEBUG(3,("Chained message\n")); + show_msg(ibuf); + + /* process the request */ + outsize = switch_message(type,ibuf,obuf,smb_wct+insize_remaining, + bufsize-chain_size); + + /* copy the new reply header over the old one, but preserve + the smb_com field */ + memcpy(outbuf+smb_com+1,obuf+smb_com+1,smb_wct-(smb_com+1)); + + /* and copy the data from the reply to the right spot */ + memcpy(outbuf2,obuf+smb_wct,outsize - smb_wct); + + /* free the allocated buffers */ + if (ibuf) free(ibuf-SMB_ALIGNMENT); + if (obuf) free(obuf-SMB_ALIGNMENT); + + in_chain = was_inchain; + + /* return how much extra has been added to the packet */ + return(outsize - smb_wct); +} + + + +/**************************************************************************** + construct a reply to the incoming packet +****************************************************************************/ +int construct_reply(char *inbuf,char *outbuf,int size,int bufsize) +{ + int type = CVAL(inbuf,smb_com); + int outsize = 0; + int msg_type = CVAL(inbuf,0); + + smb_last_time = time(NULL); + + chain_size = 0; + + bzero(outbuf,smb_size); + + if (msg_type != 0) + return(reply_special(inbuf,outbuf)); + + CVAL(outbuf,smb_com) = CVAL(inbuf,smb_com); + set_message(outbuf,0,0,True); + + memcpy(outbuf+4,inbuf+4,4); + CVAL(outbuf,smb_rcls) = SUCCESS; + CVAL(outbuf,smb_reh) = 0; + CVAL(outbuf,smb_flg) = 0x80 | (CVAL(inbuf,smb_flg) & 0x8); /* bit 7 set + means a reply */ + SSVAL(outbuf,smb_flg2,1); /* say we support long filenames */ + SSVAL(outbuf,smb_err,SUCCESS); + SSVAL(outbuf,smb_tid,SVAL(inbuf,smb_tid)); + SSVAL(outbuf,smb_pid,SVAL(inbuf,smb_pid)); + SSVAL(outbuf,smb_uid,SVAL(inbuf,smb_uid)); + SSVAL(outbuf,smb_mid,SVAL(inbuf,smb_mid)); + + outsize = switch_message(type,inbuf,outbuf,size,bufsize); + + if(outsize > 4) + smb_setlen(outbuf,outsize - 4); + return(outsize); +} + + +/**************************************************************************** + process commands from the client +****************************************************************************/ +void process(void ) +{ + static int trans_num = 0; + int nread; + extern struct from_host Client_info; + extern int Client; + + fromhost(Client,&Client_info); + + InBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + OutBuffer = (char *)malloc(BUFFER_SIZE + SAFETY_MARGIN); + if ((InBuffer == NULL) || (OutBuffer == NULL)) + return; + + InBuffer += SMB_ALIGNMENT; + OutBuffer += SMB_ALIGNMENT; + +#if PRIME_NMBD + DEBUG(3,("priming nmbd\n")); + { + struct in_addr ip; + ip = *interpret_addr2("localhost"); + if (zero_ip(ip)) ip = *interpret_addr2("127.0.0.1"); + *OutBuffer = 0; + send_one_packet(OutBuffer,1,ip,137,SOCK_DGRAM); + } +#endif + + last_user.cnum = -1; + + while (True) + { + int32 len; + int msg_type; + int msg_flags; + int type; + int deadtime = lp_deadtime()*60; + int counter; + int last_keepalive=0; + + if (deadtime <= 0) + deadtime = DEFAULT_SMBD_TIMEOUT; + + if (lp_readprediction()) + do_read_prediction(); + + { + extern pstring share_del_pending; + if (*share_del_pending) { + unbecome_user(); + if (!unlink(share_del_pending)) + DEBUG(3,("Share file deleted %s\n",share_del_pending)); + else + DEBUG(2,("Share del failed of %s\n",share_del_pending)); + share_del_pending[0] = 0; + } + } + + if (share_mode_pending) { + unbecome_user(); + check_share_modes(); + share_mode_pending=False; + } + + errno = 0; + + for (counter=SMBD_SELECT_LOOP; + !receive_smb(Client,InBuffer,SMBD_SELECT_LOOP*1000); + counter += SMBD_SELECT_LOOP) + { + int i; + time_t t; + BOOL allidle = True; + extern int keepalive; + + /* check for socket failure */ + if (errno == EBADF) { + DEBUG(3,("%s Bad file descriptor - exiting\n",timestring())); + return; + } + + t = time(NULL); + + /* become root again if waiting */ + unbecome_user(); + + /* check for smb.conf reload */ + if (!(counter%SMBD_RELOAD_CHECK)) + reload_services(True); + + /* check the share modes every 10 secs */ + if (!(counter%SHARE_MODES_CHECK)) + check_share_modes(); + + /* clean the share modes every 5 minutes */ + if (!(counter%SHARE_MODES_CLEAN)) + clean_share_files(); + + /* automatic timeout if all connections are closed */ + if (num_connections_open==0 && counter >= IDLE_CLOSED_TIMEOUT) { + DEBUG(2,("%s Closing idle connection\n",timestring())); + return; + } + + if (keepalive && (counter-last_keepalive)>keepalive) { + if (!send_keepalive(Client)) { + DEBUG(2,("%s Keepalive failed - exiting\n",timestring())); + return; + } + last_keepalive = counter; + } + + /* check for connection timeouts */ + for (i=0;i<MAX_CONNECTIONS;i++) + if (Connections[i].open) + { + /* close dirptrs on connections that are idle */ + if ((t-Connections[i].lastused)>DPTR_IDLE_TIMEOUT) + dptr_idlecnum(i); + + if (Connections[i].num_files_open > 0 || + (t-Connections[i].lastused)<deadtime) + allidle = False; + } + + if (allidle && num_connections_open>0) { + DEBUG(2,("%s Closing idle connection 2\n",timestring())); + return; + } + } + + msg_type = CVAL(InBuffer,0); + msg_flags = CVAL(InBuffer,1); + type = CVAL(InBuffer,smb_com); + + len = smb_len(InBuffer); + + DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len)); + + nread = len + 4; + + DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread)); + +#ifdef WITH_VTP + if(trans_num == 1 && VT_Check(InBuffer)) { + VT_Process(); + return; + } +#endif + + + if (msg_type == 0) + show_msg(InBuffer); + + nread = construct_reply(InBuffer,OutBuffer,nread,maxxmit); + + if(nread > 0) { + if (CVAL(OutBuffer,0) == 0) + show_msg(OutBuffer); + + if (nread != smb_len(OutBuffer) + 4) + { + DEBUG(0,("ERROR: Invalid message response size! %d %d\n", + nread, + smb_len(OutBuffer))); + } + else + send_smb(Client,OutBuffer); + } + trans_num++; + } +} + + +/**************************************************************************** + initialise connect, service and file structs +****************************************************************************/ +static void init_structs(void ) +{ + int i; + get_myname(myhostname,&myip); + + for (i=0;i<MAX_CONNECTIONS;i++) + { + Connections[i].open = False; + Connections[i].num_files_open=0; + Connections[i].lastused=0; + Connections[i].used=False; + string_init(&Connections[i].user,""); + string_init(&Connections[i].dirpath,""); + string_init(&Connections[i].connectpath,""); + string_init(&Connections[i].origpath,""); + } + + for (i=0;i<MAX_OPEN_FILES;i++) + { + Files[i].open = False; + string_init(&Files[i].name,""); + } + + init_dptrs(); +} + +/**************************************************************************** +usage on the program +****************************************************************************/ +void usage(char *pname) +{ + DEBUG(0,("Incorrect program usage - are you sure the command line is correct?\n")); + + printf("Usage: %s [-D] [-p port] [-d debuglevel] [-l log basename] [-s services file]\n",pname); + printf("Version %s\n",VERSION); + printf("\t-D become a daemon\n"); + printf("\t-p port listen on the specified port\n"); + printf("\t-d debuglevel set the debuglevel\n"); + printf("\t-l log basename. Basename for log/debug files\n"); + printf("\t-s services file. Filename of services file\n"); + printf("\t-P passive only\n"); + printf("\t-a overwrite log file, don't append\n"); + printf("\n"); +} + + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc,char *argv[]) +{ + extern BOOL append_log; + /* shall I run as a daemon */ + BOOL is_daemon = False; + int port = 139; + int opt; + extern char *optarg; + +#ifdef NEED_AUTH_PARAMETERS + set_auth_parameters(argc,argv); +#endif + +#ifdef SecureWare + setluid(0); +#endif + + append_log = True; + + TimeInit(); + + strcpy(debugf,SMBLOGFILE); + + setup_logging(argv[0],False); + + charset_initialise(); + + /* make absolutely sure we run as root - to handle cases whre people + are crazy enough to have it setuid */ +#ifdef USE_SETRES + setresuid(0,0,0); +#else + setuid(0); + seteuid(0); + setuid(0); + seteuid(0); +#endif + + fault_setup(exit_server); + + umask(0777 & ~DEF_CREATE_MASK); + + initial_uid = geteuid(); + initial_gid = getegid(); + + if (initial_gid != 0 && initial_uid == 0) + { +#ifdef HPUX + setresgid(0,0,0); +#else + setgid(0); + setegid(0); +#endif + } + + initial_uid = geteuid(); + initial_gid = getegid(); + + + /* this is for people who can't start the program correctly */ + while (argc > 1 && (*argv[1] != '-')) + { + argv++; + argc--; + } + + while ((opt = getopt(argc, argv, "O:i:l:s:d:Dp:hPa")) != EOF) + switch (opt) + { + case 'O': + strcpy(user_socket_options,optarg); + break; + case 'i': + strcpy(scope,optarg); + break; + case 'P': + { + extern BOOL passive; + passive = True; + } + break; + case 's': + strcpy(servicesf,optarg); + break; + case 'l': + strcpy(debugf,optarg); + break; + case 'a': + { + extern BOOL append_log; + append_log = !append_log; + } + break; + case 'D': + is_daemon = True; + break; + case 'd': + if (*optarg == 'A') + DEBUGLEVEL = 10000; + else + DEBUGLEVEL = atoi(optarg); + break; + case 'p': + port = atoi(optarg); + break; + case 'h': + usage(argv[0]); + exit(0); + break; + default: + usage(argv[0]); + exit(1); + } + + reopen_logs(); + + DEBUG(2,("%s smbd version %s started\n",timestring(),VERSION)); + DEBUG(2,("Copyright Andrew Tridgell 1992-1995\n")); + + GetWd(OriginalDir); + +#ifndef NO_GETRLIMIT +#ifdef RLIMIT_NOFILE + { + struct rlimit rlp; + getrlimit(RLIMIT_NOFILE, &rlp); + rlp.rlim_cur = (MAX_OPEN_FILES>rlp.rlim_max)? rlp.rlim_max:MAX_OPEN_FILES; + setrlimit(RLIMIT_NOFILE, &rlp); + getrlimit(RLIMIT_NOFILE, &rlp); + DEBUG(3,("Maximum number of open files per session is %d\n",rlp.rlim_cur)); + } +#endif +#endif + + + DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n", + getuid(),getgid(),geteuid(),getegid())); + + if (sizeof(uint16) < 2 || sizeof(uint32) < 4) + { + DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n")); + exit(1); + } + + init_structs(); + + if (!reload_services(False)) + return(-1); + +#ifndef NO_SIGNAL_TEST + signal(SIGHUP,SIGNAL_CAST sig_hup); +#endif + + DEBUG(3,("%s loaded services\n",timestring())); + + if (!is_daemon && !is_a_socket(0)) + { + DEBUG(0,("standard input is not a socket, assuming -D option\n")); + is_daemon = True; + } + + if (is_daemon) + { + DEBUG(3,("%s becoming a daemon\n",timestring())); + become_daemon(); + } + + if (open_sockets(is_daemon,port)) + { + /* possibly reload the services file. */ + reload_services(True); + + maxxmit = MIN(lp_maxxmit(),BUFFER_SIZE); + + if (*lp_rootdir()) + { + if (sys_chroot(lp_rootdir()) == 0) + DEBUG(2,("%s changed root to %s\n",timestring(),lp_rootdir())); + } + + process(); + close_sockets(); + } + exit_server("normal exit"); + return(0); +} + + diff --git a/source3/smbd/smbrun.c b/source3/smbd/smbrun.c new file mode 100644 index 0000000000..df12ae1f85 --- /dev/null +++ b/source3/smbd/smbrun.c @@ -0,0 +1,96 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + external program running routine + Copyright (C) Andrew Tridgell 1992-1995 + + 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. +*/ + +#include "includes.h" + + +/******************************************************************* +close the low 3 fd's and open dev/null in their place +********************************************************************/ +static void close_fds(void) +{ + int fd; + int i; + close(0); close(1); close(2); + /* try and use up these file descriptors, so silly + library routines writing to stdout etc won't cause havoc */ + for (i=0;i<3;i++) { + fd = open("/dev/null",O_RDWR,0); + if (fd < 0) fd = open("/dev/null",O_WRONLY,0); + if (fd != i) return; + } +} + + +/* +This is a wrapper around the system call to allow commands to run correctly +as non root from a program which is switching between root and non-root + +It takes one argument as argv[1] and runs it after becoming a non-root +user +*/ +int main(int argc,char *argv[]) +{ + close_fds(); + + if (getuid() != geteuid()) + { + int uid,gid; + + if (getuid() == 0) + uid = geteuid(); + else + uid = getuid(); + + if (getgid() == 0) + gid = getegid(); + else + gid = getgid(); + +#ifdef USE_SETRES + setresgid(0,0,0); + setresuid(0,0,0); + setresgid(gid,gid,gid); + setresuid(uid,uid,uid); +#else + setuid(0); + seteuid(0); + setgid(gid); + setegid(gid); + setuid(uid); + seteuid(uid); +#endif + + if (getuid() != uid) + return(3); + } + + if (geteuid() != getuid()) + return(1); + + if (argc < 2) + return(2); + + /* this is to make sure that the system() call doesn't run forever */ + alarm(30); + + return(system(argv[1])); +} diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c new file mode 100644 index 0000000000..9d02123cf8 --- /dev/null +++ b/source3/smbd/trans2.c @@ -0,0 +1,1646 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + SMB transaction2 handling + Copyright (C) Jeremy Allison 1994 + + Extensively modified by Andrew Tridgell, 1995 + + 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. +*/ + +#include "includes.h" +#include "loadparm.h" +#include "trans2.h" + +extern int DEBUGLEVEL; +extern int Protocol; +extern connection_struct Connections[]; +extern files_struct Files[]; +extern BOOL case_sensitive; +extern int Client; + +/**************************************************************************** + Send the required number of replies back. + We assume all fields other than the data fields are + set correctly for the type of call. + HACK ! Always assumes smb_setup field is zero. +****************************************************************************/ +static int send_trans2_replies(char *outbuf, int bufsize, char *params, + int paramsize, char *pdata, int datasize) +{ + /* As we are using a protocol > LANMAN1 then the maxxmit + variable must have been set in the sessetupX call. + This takes precedence over the max_xmit field in the + global struct. These different max_xmit variables should + be merged as this is now too confusing */ + + extern int maxxmit; + int data_to_send = datasize; + int params_to_send = paramsize; + int useable_space; + char *pp = params; + char *pd = pdata; + int params_sent_thistime, data_sent_thistime, total_sent_thistime; + int alignment_offset = 1; + + /* Initially set the wcnt area to be 10 - this is true for all + trans2 replies */ + set_message(outbuf,10,0,True); + + /* If there genuinely are no parameters or data to send just send + the empty packet */ + if(params_to_send == 0 && data_to_send == 0) + { + send_smb(Client,outbuf); + return 0; + } + + /* Space is bufsize minus Netbios over TCP header minus SMB header */ + /* The + 1 is to align the param and data bytes on an even byte + boundary. NT 4.0 Beta needs this to work correctly. */ + useable_space = bufsize - ((smb_buf(outbuf)+alignment_offset) - outbuf); + useable_space = MIN(useable_space, maxxmit); /* XXX is this needed? correct? */ + + while( params_to_send || data_to_send) + { + /* Calculate whether we will totally or partially fill this packet */ + total_sent_thistime = params_to_send + data_to_send + alignment_offset; + total_sent_thistime = MIN(total_sent_thistime, useable_space); + + set_message(outbuf, 10, total_sent_thistime, True); + + /* Set total params and data to be sent */ + SSVAL(outbuf,smb_tprcnt,paramsize); + SSVAL(outbuf,smb_tdrcnt,datasize); + + /* Calculate how many parameters and data we can fit into + this packet. Parameters get precedence */ + + params_sent_thistime = MIN(params_to_send,useable_space); + data_sent_thistime = useable_space - params_sent_thistime; + data_sent_thistime = MIN(data_sent_thistime,data_to_send); + + SSVAL(outbuf,smb_prcnt, params_sent_thistime); + if(params_sent_thistime == 0) + { + SSVAL(outbuf,smb_proff,0); + SSVAL(outbuf,smb_prdisp,0); + } else { + /* smb_proff is the offset from the start of the SMB header to the + parameter bytes, however the first 4 bytes of outbuf are + the Netbios over TCP header. Thus use smb_base() to subtract + them from the calculation */ + SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); + /* Absolute displacement of param bytes sent in this packet */ + SSVAL(outbuf,smb_prdisp,pp - params); + } + + SSVAL(outbuf,smb_drcnt, data_sent_thistime); + if(data_sent_thistime == 0) + { + SSVAL(outbuf,smb_droff,0); + SSVAL(outbuf,smb_drdisp, 0); + } else { + /* The offset of the data bytes is the offset of the + parameter bytes plus the number of parameters being sent this time */ + SSVAL(outbuf,smb_droff,((smb_buf(outbuf)+alignment_offset) - + smb_base(outbuf)) + params_sent_thistime); + SSVAL(outbuf,smb_drdisp, pd - pdata); + } + + /* Copy the param bytes into the packet */ + if(params_sent_thistime) + memcpy((smb_buf(outbuf)+alignment_offset),pp,params_sent_thistime); + /* Copy in the data bytes */ + if(data_sent_thistime) + memcpy(smb_buf(outbuf)+alignment_offset+params_sent_thistime,pd,data_sent_thistime); + + DEBUG(9,("t2_rep: params_sent_thistime = %d, data_sent_thistime = %d, useable_space = %d\n", + params_sent_thistime, data_sent_thistime, useable_space)); + DEBUG(9,("t2_rep: params_to_send = %d, data_to_send = %d, paramsize = %d, datasize = %d\n", + params_to_send, data_to_send, paramsize, datasize)); + + /* Send the packet */ + send_smb(Client,outbuf); + + pp += params_sent_thistime; + pd += data_sent_thistime; + + params_to_send -= params_sent_thistime; + data_to_send -= data_sent_thistime; + + /* Sanity check */ + if(params_to_send < 0 || data_to_send < 0) + { + DEBUG(2,("send_trans2_replies failed sanity check pts = %d, dts = %d\n!!!", + params_to_send, data_to_send)); + return -1; + } + } + + return 0; +} + + +/**************************************************************************** + reply to a TRANSACT2_OPEN +****************************************************************************/ +static int call_trans2open(char *inbuf, char *outbuf, int bufsize, int cnum, + char **pparams, char **ppdata) +{ + char *params = *pparams; + int16 open_mode = SVAL(params, 2); + int16 open_attr = SVAL(params,6); +#if 0 + BOOL return_additional_info = BITSETW(params,0); + int16 open_sattr = SVAL(params, 4); + time_t open_time = make_unix_date3(params+8); +#endif + int16 open_ofun = SVAL(params,12); + int32 open_size = IVAL(params,14); + char *pname = ¶ms[28]; + int16 namelen = strlen(pname)+1; + + pstring fname; + int fnum = -1; + int unixmode; + int size=0,fmode=0,mtime=0,rmode; + int32 inode = 0; + struct stat sbuf; + int smb_action = 0; + + StrnCpy(fname,pname,namelen); + + DEBUG(3,("trans2open %s cnum=%d mode=%d attr=%d ofun=%d size=%d\n", + fname,cnum,open_mode, open_attr, open_ofun, open_size)); + + /* XXXX we need to handle passed times, sattr and flags */ + + unix_convert(fname,cnum); + + fnum = find_free_file(); + if (fnum < 0) + return(ERROR(ERRSRV,ERRnofids)); + + if (!check_name(fname,cnum)) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + unixmode = unix_mode(cnum,open_attr | aARCH); + + + open_file_shared(fnum,cnum,fname,open_mode,open_ofun,unixmode, + &rmode,&smb_action); + + if (!Files[fnum].open) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (fstat(Files[fnum].fd,&sbuf) != 0) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + size = sbuf.st_size; + fmode = dos_mode(cnum,fname,&sbuf); + mtime = sbuf.st_mtime; + inode = sbuf.st_ino; + if (fmode & aDIR) { + close_file(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + + /* Realloc the size of parameters and data we will return */ + params = *pparams = Realloc(*pparams, 28); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + bzero(params,28); + SSVAL(params,0,fnum); + SSVAL(params,2,fmode); + put_dos_date2(params,4, mtime); + SIVAL(params,8, size); + SSVAL(params,12,rmode); + + SSVAL(params,18,smb_action); + SIVAL(params,20,inode); + + /* Send the required number of replies */ + send_trans2_replies(outbuf, bufsize, params, 28, *ppdata, 0); + + return -1; +} + +/**************************************************************************** + get a level dependent lanman2 dir entry. +****************************************************************************/ +static int get_lanman2_dir_entry(int cnum,char *path_mask,int dirtype,int info_level, + int requires_resume_key, + BOOL dont_descend,char **ppdata, + char *base_data, int space_remaining, + BOOL *out_of_space, + int *last_name_off) +{ + char *dname; + BOOL found = False; + struct stat sbuf; + pstring mask; + pstring pathreal; + pstring fname; + BOOL matched; + char *p, *pdata = *ppdata; + int reskey=0, prev_dirpos=0; + int mode=0; + uint32 size=0,len; + uint32 mdate=0, adate=0, cdate=0; + char *name_ptr; + BOOL isrootdir = (strequal(Connections[cnum].dirpath,"./") || + strequal(Connections[cnum].dirpath,".") || + strequal(Connections[cnum].dirpath,"/")); + BOOL was_8_3; + + *fname = 0; + *out_of_space = False; + + if (!Connections[cnum].dirptr) + return(False); + + p = strrchr(path_mask,'/'); + if(p != NULL) + { + if(p[1] == '\0') + strcpy(mask,"*.*"); + else + strcpy(mask, p+1); + } + else + strcpy(mask, path_mask); + + while (!found) + { + /* Needed if we run out of space */ + prev_dirpos = TellDir(Connections[cnum].dirptr); + dname = ReadDirName(Connections[cnum].dirptr); + + reskey = TellDir(Connections[cnum].dirptr); + + DEBUG(6,("get_lanman2_dir_entry:readdir on dirptr 0x%x now at offset %d\n", + Connections[cnum].dirptr,TellDir(Connections[cnum].dirptr))); + + if (!dname) + return(False); + + matched = False; + + strcpy(fname,dname); + + if(mask_match(fname, mask, case_sensitive, True)) + { + BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); + if (dont_descend && !isdots) + continue; + + if (isrootdir && isdots) + continue; + + strcpy(pathreal,Connections[cnum].dirpath); + strcat(pathreal,"/"); + strcat(pathreal,fname); + if (sys_stat(pathreal,&sbuf) != 0) + { + DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno))); + continue; + } + + mode = dos_mode(cnum,pathreal,&sbuf); + + if (((mode & ~dirtype) & (aHIDDEN | aSYSTEM | aDIR)) != 0) + { + DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype)); + continue; + } + size = sbuf.st_size; + mdate = sbuf.st_mtime; + adate = sbuf.st_atime; + cdate = sbuf.st_ctime; + if(mode & aDIR) + size = 0; + + DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname)); + + found = True; + } + } + + +#ifndef KANJI + unix2dos_format(fname, True); +#endif + + p = pdata; + name_ptr = p; + + name_map_mangle(fname,False,SNUM(cnum)); + + switch (info_level) + { + case 1: + if(requires_resume_key) { + SIVAL(p,0,reskey); + p += 4; + } + put_dos_date2(p,l1_fdateCreation,cdate); + put_dos_date2(p,l1_fdateLastAccess,adate); + put_dos_date2(p,l1_fdateLastWrite,mdate); + SIVAL(p,l1_cbFile,size); + SIVAL(p,l1_cbFileAlloc,ROUNDUP(size,1024)); + SSVAL(p,l1_attrFile,mode); + SCVAL(p,l1_cchName,strlen(fname)); + strcpy(p + l1_achName, fname); + name_ptr = p + l1_achName; + p += l1_achName + strlen(fname) + 1; + break; + + case 2: + /* info_level 2 */ + if(requires_resume_key) { + SIVAL(p,0,reskey); + p += 4; + } + put_dos_date2(p,l2_fdateCreation,cdate); + put_dos_date2(p,l2_fdateLastAccess,adate); + put_dos_date2(p,l2_fdateLastWrite,mdate); + SIVAL(p,l2_cbFile,size); + SIVAL(p,l2_cbFileAlloc,ROUNDUP(size,1024)); + SSVAL(p,l2_attrFile,mode); + SIVAL(p,l2_cbList,0); /* No extended attributes */ + SCVAL(p,l2_cchName,strlen(fname)); + strcpy(p + l2_achName, fname); + name_ptr = p + l2_achName; + p += l2_achName + strlen(fname) + 1; + break; + + case 3: + SIVAL(p,0,reskey); + put_dos_date2(p,4,cdate); + put_dos_date2(p,8,adate); + put_dos_date2(p,12,mdate); + SIVAL(p,16,size); + SIVAL(p,20,ROUNDUP(size,1024)); + SSVAL(p,24,mode); + SIVAL(p,26,4); + CVAL(p,30) = strlen(fname); + strcpy(p+31, fname); + name_ptr = p+31; + p += 31 + strlen(fname) + 1; + break; + + case 4: + if(requires_resume_key) { + SIVAL(p,0,reskey); + p += 4; + } + SIVAL(p,0,33+strlen(fname)+1); + put_dos_date2(p,4,cdate); + put_dos_date2(p,8,adate); + put_dos_date2(p,12,mdate); + SIVAL(p,16,size); + SIVAL(p,20,ROUNDUP(size,1024)); + SSVAL(p,24,mode); + CVAL(p,32) = strlen(fname); + strcpy(p + 33, fname); + name_ptr = p+33; + p += 33 + strlen(fname) + 1; + break; + + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + was_8_3 = is_8_3(fname); + len = 94+strlen(fname); + len = (len + 3) & ~3; + SIVAL(p,0,len); p += 4; + SIVAL(p,0,reskey); p += 4; + put_long_date(p,cdate); p += 8; + put_long_date(p,adate); p += 8; + put_long_date(p,mdate); p += 8; + put_long_date(p,mdate); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,mode); p += 4; + SIVAL(p,0,strlen(fname)); p += 4; + SIVAL(p,0,0); p += 4; + if (!was_8_3) { +#ifndef KANJI + strcpy(p+2,unix2dos_format(fname,False)); +#else + strcpy(p+2,fname); +#endif + if (!name_map_mangle(p+2,True,SNUM(cnum))) + (p+2)[12] = 0; + } else + *(p+2) = 0; + strupper(p+2); + SSVAL(p,0,strlen(p+2)); + p += 2 + 24; + /* name_ptr = p; */ + strcpy(p,fname); p += strlen(p); + p = pdata + len; + break; + + case SMB_FIND_FILE_DIRECTORY_INFO: + len = 64+strlen(fname); + len = (len + 3) & ~3; + SIVAL(p,0,len); p += 4; + SIVAL(p,0,reskey); p += 4; + put_long_date(p,cdate); p += 8; + put_long_date(p,adate); p += 8; + put_long_date(p,mdate); p += 8; + put_long_date(p,mdate); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,mode); p += 4; + SIVAL(p,0,strlen(fname)); p += 4; + strcpy(p,fname); + p = pdata + len; + break; + + + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + len = 68+strlen(fname); + len = (len + 3) & ~3; + SIVAL(p,0,len); p += 4; + SIVAL(p,0,reskey); p += 4; + put_long_date(p,cdate); p += 8; + put_long_date(p,adate); p += 8; + put_long_date(p,mdate); p += 8; + put_long_date(p,mdate); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,size); p += 8; + SIVAL(p,0,mode); p += 4; + SIVAL(p,0,strlen(fname)); p += 4; + SIVAL(p,0,0); p += 4; + strcpy(p,fname); + p = pdata + len; + break; + + case SMB_FIND_FILE_NAMES_INFO: + len = 12+strlen(fname); + len = (len + 3) & ~3; + SIVAL(p,0,len); p += 4; + SIVAL(p,0,reskey); p += 4; + SIVAL(p,0,strlen(fname)); p += 4; + strcpy(p,fname); + p = pdata + len; + break; + + default: + return(False); + } + + + if (PTR_DIFF(p,pdata) > space_remaining) { + /* Move the dirptr back to prev_dirpos */ + SeekDir(Connections[cnum].dirptr, prev_dirpos); + *out_of_space = True; + DEBUG(9,("get_lanman2_dir_entry: out of space\n")); + return False; /* Not finished - just out of space */ + } + + /* Setup the last_filename pointer, as an offset from base_data */ + *last_name_off = PTR_DIFF(name_ptr,base_data); + /* Advance the data pointer to the next slot */ + *ppdata = p; + return(found); +} + +/**************************************************************************** + reply to a TRANS2_FINDFIRST +****************************************************************************/ +static int call_trans2findfirst(char *inbuf, char *outbuf, int bufsize, int cnum, + char **pparams, char **ppdata) +{ + /* We must be careful here that we don't return more than the + allowed number of data bytes. If this means returning fewer than + maxentries then so be it. We assume that the redirector has + enough room for the fixed number of parameter bytes it has + requested. */ + uint32 max_data_bytes = SVAL(inbuf, smb_mdrcnt); + char *params = *pparams; + char *pdata = *ppdata; + int dirtype = SVAL(params,0); + int maxentries = SVAL(params,2); + BOOL close_after_first = BITSETW(params+4,0); + BOOL close_if_end = BITSETW(params+4,1); + BOOL requires_resume_key = BITSETW(params+4,2); + int info_level = SVAL(params,6); + pstring directory; + pstring mask; + char *p, *wcard; + int last_name_off=0; + int dptr_num = -1; + int numentries = 0; + int i; + BOOL finished = False; + BOOL dont_descend = False; + BOOL out_of_space = False; + int space_remaining; + + *directory = *mask = 0; + + DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, close_if_end = %d requires_resume_key = %d level = %d, max_data_bytes = %d\n", + dirtype, maxentries, close_after_first, close_if_end, requires_resume_key, + info_level, max_data_bytes)); + + switch (info_level) + { + case 1: + case 2: + case 3: + case 4: + case SMB_FIND_FILE_DIRECTORY_INFO: + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + case SMB_FIND_FILE_NAMES_INFO: + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + break; + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + strcpy(directory, params + 12); /* Complete directory path with + wildcard mask appended */ + + DEBUG(5,("path=%s\n",directory)); + + unix_convert(directory,cnum); + if(!check_name(directory,cnum)) { + return(ERROR(ERRDOS,ERRbadpath)); + } + + p = strrchr(directory,'/'); + if(p == NULL) { + strcpy(mask,directory); + strcpy(directory,"./"); + } else { + strcpy(mask,p+1); + *p = 0; + } + + DEBUG(5,("dir=%s, mask = %s\n",directory, mask)); + + pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024); + if(!*ppdata) + return(ERROR(ERRDOS,ERRnomem)); + bzero(pdata,max_data_bytes); + + /* Realloc the params space */ + params = *pparams = Realloc(*pparams, 10); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + dptr_num = dptr_create(cnum,directory, True ,SVAL(inbuf,smb_pid)); + if (dptr_num < 0) + return(ERROR(ERRDOS,ERRbadpath)); + + /* convert the formatted masks */ + { + p = mask; + while (*p) { + if (*p == '<') *p = '*'; + if (*p == '>') *p = '?'; + if (*p == '"') *p = '.'; + p++; + } + } + + /* a special case for 16 bit apps */ + if (strequal(mask,"????????.???")) strcpy(mask,"*"); + + /* handle broken clients that send us old 8.3 format */ + string_sub(mask,"????????","*"); + string_sub(mask,".???",".*"); + + /* Save the wildcard match and attribs we are using on this directory - + needed as lanman2 assumes these are being saved between calls */ + + if(!(wcard = strdup(mask))) { + dptr_close(dptr_num); + return(ERROR(ERRDOS,ERRnomem)); + } + + dptr_set_wcard(dptr_num, wcard); + dptr_set_attr(dptr_num, dirtype); + + DEBUG(4,("dptr_num is %d, wcard = %s, attr = %d\n",dptr_num, wcard, dirtype)); + + /* We don't need to check for VOL here as this is returned by + a different TRANS2 call. */ + + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n", + Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); + if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive)) + dont_descend = True; + + p = pdata; + space_remaining = max_data_bytes; + out_of_space = False; + + for (i=0;(i<maxentries) && !finished && !out_of_space;i++) + { + + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) + { + out_of_space = True; + finished = False; + } + else + { + finished = + !get_lanman2_dir_entry(cnum,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, + &last_name_off); + } + + if (finished && out_of_space) + finished = False; + + if (!finished && !out_of_space) + numentries++; + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } + + /* Check if we can close the dirptr */ + if(close_after_first || (finished && close_if_end)) + { + dptr_close(dptr_num); + DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); + dptr_num = -1; + } + + /* At this point pdata points to numentries directory entries. */ + + /* Set up the return parameter block */ + SSVAL(params,0,dptr_num); + SSVAL(params,2,numentries); + SSVAL(params,4,finished); + SSVAL(params,6,0); /* Never an EA error */ + SSVAL(params,8,last_name_off); + + send_trans2_replies( outbuf, bufsize, params, 10, pdata, PTR_DIFF(p,pdata)); + + if ((! *directory) && dptr_path(dptr_num)) + sprintf(directory,"(%s)",dptr_path(dptr_num)); + + DEBUG(4,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n", + timestring(), + smb_fn_name(CVAL(inbuf,smb_com)), + mask,directory,cnum,dirtype,numentries)); + + return(-1); +} + + +/**************************************************************************** + reply to a TRANS2_FINDNEXT +****************************************************************************/ +static int call_trans2findnext(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + /* We must be careful here that we don't return more than the + allowed number of data bytes. If this means returning fewer than + maxentries then so be it. We assume that the redirector has + enough room for the fixed number of parameter bytes it has + requested. */ + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); + char *params = *pparams; + char *pdata = *ppdata; + int16 dptr_num = SVAL(params,0); + int maxentries = SVAL(params,2); + uint16 info_level = SVAL(params,4); + uint32 resume_key = IVAL(params,6); + BOOL close_after_request = BITSETW(params+10,0); + BOOL close_if_end = BITSETW(params+10,1); + BOOL requires_resume_key = BITSETW(params+10,2); + BOOL continue_bit = BITSETW(params+10,3); + pstring mask; + pstring directory; + char *p; + uint16 dirtype; + int numentries = 0; + int i, last_name_off=0; + BOOL finished = False; + BOOL dont_descend = False; + BOOL out_of_space = False; + int space_remaining; + + *mask = *directory = 0; + + DEBUG(3,("call_trans2findnext: dirhandle = %d, max_data_bytes = %d, maxentries = %d, close_after_request=%d, close_if_end = %d requires_resume_key = %d resume_key = %d continue=%d level = %d\n", + dptr_num, max_data_bytes, maxentries, close_after_request, close_if_end, + requires_resume_key, resume_key, continue_bit, info_level)); + + switch (info_level) + { + case 1: + case 2: + case 3: + case 4: + case SMB_FIND_FILE_DIRECTORY_INFO: + case SMB_FIND_FILE_FULL_DIRECTORY_INFO: + case SMB_FIND_FILE_NAMES_INFO: + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: + break; + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024); + if(!*ppdata) + return(ERROR(ERRDOS,ERRnomem)); + bzero(pdata,max_data_bytes); + + /* Realloc the params space */ + params = *pparams = Realloc(*pparams, 6*SIZEOFWORD); + if(!params) + return(ERROR(ERRDOS,ERRnomem)); + + /* Check that the dptr is valid */ + if(!(Connections[cnum].dirptr = dptr_fetch_lanman2(params, dptr_num))) + return(ERROR(ERRDOS,ERRnofiles)); + + string_set(&Connections[cnum].dirpath,dptr_path(dptr_num)); + + /* Get the wildcard mask from the dptr */ + if((p = dptr_wcard(dptr_num))== NULL) { + DEBUG(2,("dptr_num %d has no wildcard\n", dptr_num)); + return (ERROR(ERRDOS,ERRnofiles)); + } + strcpy(mask, p); + strcpy(directory,Connections[cnum].dirpath); + + /* Get the attr mask from the dptr */ + dirtype = dptr_attr(dptr_num); + + DEBUG(3,("dptr_num is %d, mask = %s, attr = %x, dirptr=(0x%X,%d)\n", + dptr_num, mask, dirtype, + Connections[cnum].dirptr, + TellDir(Connections[cnum].dirptr))); + + /* We don't need to check for VOL here as this is returned by + a different TRANS2 call. */ + + DEBUG(8,("dirpath=<%s> dontdescend=<%s>\n",Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)))); + if (in_list(Connections[cnum].dirpath,lp_dontdescend(SNUM(cnum)),case_sensitive)) + dont_descend = True; + + p = pdata; + space_remaining = max_data_bytes; + out_of_space = False; + + /* If we have a resume key - seek to the correct position. */ + if(requires_resume_key && !continue_bit) + SeekDir(Connections[cnum].dirptr, resume_key); + + for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) + { + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) + { + out_of_space = True; + finished = False; + } + else + { + finished = + !get_lanman2_dir_entry(cnum,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, + &last_name_off); + } + + if (finished && out_of_space) + finished = False; + + if (!finished && !out_of_space) + numentries++; + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } + + /* Check if we can close the dirptr */ + if(close_after_request || (finished && close_if_end)) + { + dptr_close(dptr_num); /* This frees up the saved mask */ + DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); + dptr_num = -1; + } + + + /* Set up the return parameter block */ + SSVAL(params,0,numentries); + SSVAL(params,2,finished); + SSVAL(params,4,0); /* Never an EA error */ + SSVAL(params,6,last_name_off); + + send_trans2_replies( outbuf, bufsize, params, 8, pdata, PTR_DIFF(p,pdata)); + + if ((! *directory) && dptr_path(dptr_num)) + sprintf(directory,"(%s)",dptr_path(dptr_num)); + + DEBUG(3,("%s %s mask=%s directory=%s cnum=%d dirtype=%d numentries=%d\n", + timestring(), + smb_fn_name(CVAL(inbuf,smb_com)), + mask,directory,cnum,dirtype,numentries)); + + return(-1); +} + +/**************************************************************************** + reply to a TRANS2_QFSINFO (query filesystem info) +****************************************************************************/ +static int call_trans2qfsinfo(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + char *pdata = *ppdata; + char *params = *pparams; + uint16 info_level = SVAL(params,0); + int data_len; + struct stat st; + char *vname = volume_label(SNUM(cnum)); + + DEBUG(3,("call_trans2qfsinfo: cnum = %d, level = %d\n", cnum, info_level)); + + if(sys_stat(".",&st)!=0) { + DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno))); + return (ERROR(ERRSRV,ERRinvdevice)); + } + + pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024); + + switch (info_level) + { + case 1: + { + int dfree,dsize,bsize; + data_len = 18; + sys_disk_free(".",&bsize,&dfree,&dsize); + SIVAL(pdata,l1_idFileSystem,st.st_dev); + SIVAL(pdata,l1_cSectorUnit,bsize/512); + SIVAL(pdata,l1_cUnit,dsize); + SIVAL(pdata,l1_cUnitAvail,dfree); + SSVAL(pdata,l1_cbSector,512); + DEBUG(5,("call_trans2qfsinfo : bsize=%d, id=%x, cSectorUnit=%d, cUnit=%d, cUnitAvail=%d, cbSector=%d\n", + bsize, st.st_dev, bsize/512, dsize, dfree, 512)); + break; + } + case 2: + { + /* Return volume name */ + int volname_len = MIN(strlen(vname),11); + data_len = l2_vol_szVolLabel + volname_len + 1; + put_dos_date2(pdata,l2_vol_fdateCreation,st.st_ctime); + SCVAL(pdata,l2_vol_cch,volname_len); + StrnCpy(pdata+l2_vol_szVolLabel,vname,volname_len); + DEBUG(5,("call_trans2qfsinfo : time = %x, namelen = %d, name = %s\n",st.st_ctime,volname_len, + pdata+l2_vol_szVolLabel)); + break; + } + case SMB_QUERY_FS_ATTRIBUTE_INFO: + data_len = 12 + 2*strlen(FSTYPE_STRING); + SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */ + SIVAL(pdata,4,128); /* Max filename component length */ + SIVAL(pdata,8,2*strlen(FSTYPE_STRING)); + PutUniCode(pdata+12,FSTYPE_STRING); + break; + case SMB_QUERY_FS_LABEL_INFO: + data_len = 4 + strlen(vname); + SIVAL(pdata,0,strlen(vname)); + strcpy(pdata+4,vname); + break; + case SMB_QUERY_FS_VOLUME_INFO: + data_len = 17 + strlen(vname); + SIVAL(pdata,12,strlen(vname)); + strcpy(pdata+17,vname); + break; + case SMB_QUERY_FS_SIZE_INFO: + { + int dfree,dsize,bsize; + data_len = 24; + sys_disk_free(".",&bsize,&dfree,&dsize); + SIVAL(pdata,0,dsize); + SIVAL(pdata,8,dfree); + SIVAL(pdata,16,bsize/512); + SIVAL(pdata,20,512); + } + break; + case SMB_QUERY_FS_DEVICE_INFO: + data_len = 8; + SIVAL(pdata,0,0); /* dev type */ + SIVAL(pdata,4,0); /* characteristics */ + break; + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + + send_trans2_replies( outbuf, bufsize, params, 0, pdata, data_len); + + DEBUG(4,("%s %s info_level =%d\n",timestring(),smb_fn_name(CVAL(inbuf,smb_com)), info_level)); + + return -1; +} + +/**************************************************************************** + reply to a TRANS2_SETFSINFO (set filesystem info) +****************************************************************************/ +static int call_trans2setfsinfo(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + /* Just say yes we did it - there is nothing that + can be set here so it doesn't matter. */ + int outsize; + DEBUG(3,("call_trans2setfsinfo\n")); + + if (!CAN_WRITE(cnum)) + return(ERROR(ERRSRV,ERRaccess)); + + outsize = set_message(outbuf,10,0,True); + + return outsize; +} + +/**************************************************************************** + reply to a TRANS2_QFILEINFO (query file info by fileid) +****************************************************************************/ +static int call_trans2qfilepathinfo(char *inbuf, char *outbuf, int length, + int bufsize,int cnum, + char **pparams,char **ppdata, + int total_data) +{ + char *params = *pparams; + char *pdata = *ppdata; + uint16 tran_call = SVAL(inbuf, smb_setup0); + uint16 info_level; + int mode=0; + int size=0; + unsigned int data_size; + struct stat sbuf; + pstring fname1; + char *fname; + char *p; + int l,pos; + + + if (tran_call == TRANSACT2_QFILEINFO) { + int16 fnum = SVAL(params,0); + info_level = SVAL(params,2); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + fname = Files[fnum].name; + if (fstat(Files[fnum].fd,&sbuf) != 0) { + DEBUG(3,("fstat of fnum %d failed (%s)\n",fnum, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRbadfid)); + } + pos = lseek(Files[fnum].fd,0,SEEK_CUR); + } else { + /* qpathinfo */ + info_level = SVAL(params,0); + fname = &fname1[0]; + strcpy(fname,¶ms[6]); + unix_convert(fname,cnum); + if (!check_name(fname,cnum) || sys_stat(fname,&sbuf)) { + DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); + return(UNIXERROR(ERRDOS,ERRbadpath)); + } + pos = 0; + } + + + DEBUG(3,("call_trans2qfilepathinfo %s level=%d call=%d total_data=%d\n", + fname,info_level,tran_call,total_data)); + + p = strrchr(fname,'/'); + if (!p) + p = fname; + else + p++; + l = strlen(p); + mode = dos_mode(cnum,fname,&sbuf); + size = sbuf.st_size; + if (mode & aDIR) size = 0; + + params = *pparams = Realloc(*pparams,2); bzero(params,2); + data_size = 1024; + pdata = *ppdata = Realloc(*ppdata, data_size); + + if (total_data > 0 && IVAL(pdata,0) == total_data) { + /* uggh, EAs for OS2 */ + DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data)); +#if 0 + SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED); + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + return(-1); +#else + return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED)); +#endif + } + + bzero(pdata,data_size); + + switch (info_level) + { + case 1: + case 2: + data_size = (info_level==1?22:26); + put_dos_date2(pdata,l1_fdateCreation,sbuf.st_ctime); + put_dos_date2(pdata,l1_fdateLastAccess,sbuf.st_atime); + put_dos_date2(pdata,l1_fdateLastWrite,sbuf.st_mtime); + SIVAL(pdata,l1_cbFile,size); + SIVAL(pdata,l1_cbFileAlloc,ROUNDUP(size,1024)); + SSVAL(pdata,l1_attrFile,mode); + SIVAL(pdata,l1_attrFile+2,4); /* this is what OS2 does */ + break; + + case 3: + data_size = 24; + put_dos_date2(pdata,0,sbuf.st_ctime); + put_dos_date2(pdata,4,sbuf.st_atime); + put_dos_date2(pdata,8,sbuf.st_mtime); + SIVAL(pdata,12,size); + SIVAL(pdata,16,ROUNDUP(size,1024)); + SIVAL(pdata,20,mode); + break; + + case 4: + data_size = 4; + SIVAL(pdata,0,data_size); + break; + + case 6: + return(ERROR(ERRDOS,ERRbadfunc)); /* os/2 needs this */ + + case SMB_QUERY_FILE_BASIC_INFO: + data_size = 36; + put_long_date(pdata,sbuf.st_ctime); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); + put_long_date(pdata+24,sbuf.st_mtime); + SIVAL(pdata,32,mode); + break; + + case SMB_QUERY_FILE_STANDARD_INFO: + data_size = 22; + SIVAL(pdata,0,size); + SIVAL(pdata,8,size); + SIVAL(pdata,16,sbuf.st_nlink); + CVAL(pdata,20) = 0; + CVAL(pdata,21) = (mode&aDIR)?1:0; + break; + + case SMB_QUERY_FILE_EA_INFO: + data_size = 4; + break; + + case SMB_QUERY_FILE_NAME_INFO: + case SMB_QUERY_FILE_ALT_NAME_INFO: + data_size = 4 + l; + SIVAL(pdata,0,l); + strcpy(pdata+4,fname); + break; + case SMB_QUERY_FILE_ALLOCATION_INFO: + case SMB_QUERY_FILE_END_OF_FILEINFO: + data_size = 8; + SIVAL(pdata,0,size); + break; + + case SMB_QUERY_FILE_ALL_INFO: + put_long_date(pdata,sbuf.st_ctime); + put_long_date(pdata+8,sbuf.st_atime); + put_long_date(pdata+16,sbuf.st_mtime); + put_long_date(pdata+24,sbuf.st_mtime); + SIVAL(pdata,32,mode); + pdata += 40; + SIVAL(pdata,0,size); + SIVAL(pdata,8,size); + SIVAL(pdata,16,sbuf.st_nlink); + CVAL(pdata,20) = 0; + CVAL(pdata,21) = (mode&aDIR)?1:0; + pdata += 24; + pdata += 8; /* index number */ + pdata += 4; /* EA info */ + if (mode & aRONLY) + SIVAL(pdata,0,0xA9); + else + SIVAL(pdata,0,0xd01BF); + pdata += 4; + SIVAL(pdata,0,pos); /* current offset */ + pdata += 8; + SIVAL(pdata,0,mode); /* is this the right sort of mode info? */ + pdata += 4; + pdata += 4; /* alignment */ + SIVAL(pdata,0,l); + strcpy(pdata+4,fname); + pdata += 4 + l; + data_size = PTR_DIFF(pdata,(*ppdata)); + break; + + case SMB_QUERY_FILE_STREAM_INFO: + data_size = 24 + l; + SIVAL(pdata,0,pos); + SIVAL(pdata,4,size); + SIVAL(pdata,12,size); + SIVAL(pdata,20,l); + strcpy(pdata+24,fname); + break; + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + send_trans2_replies( outbuf, bufsize, params, 2, *ppdata, data_size); + + return(-1); +} + +/**************************************************************************** + reply to a TRANS2_SETFILEINFO (set file info by fileid) +****************************************************************************/ +static int call_trans2setfilepathinfo(char *inbuf, char *outbuf, int length, + int bufsize, int cnum, char **pparams, + char **ppdata, int total_data) +{ + char *params = *pparams; + char *pdata = *ppdata; + uint16 tran_call = SVAL(inbuf, smb_setup0); + uint16 info_level; + int mode=0; + int size=0; + struct utimbuf tvs; + struct stat st; + pstring fname1; + char *fname; + int fd = -1; + + if (!CAN_WRITE(cnum)) + return(ERROR(ERRSRV,ERRaccess)); + + if (tran_call == TRANSACT2_SETFILEINFO) { + int16 fnum = SVAL(params,0); + info_level = SVAL(params,2); + + CHECK_FNUM(fnum,cnum); + CHECK_ERROR(fnum); + + fname = Files[fnum].name; + fd = Files[fnum].fd; + + if(fstat(fd,&st)!=0) { + DEBUG(3,("fstat of %s failed (%s)\n", fname, strerror(errno))); + return(ERROR(ERRDOS,ERRbadpath)); + } + } else { + /* set path info */ + info_level = SVAL(params,0); + fname = fname1; + strcpy(fname,¶ms[6]); + unix_convert(fname,cnum); + if(!check_name(fname, cnum)) + return(ERROR(ERRDOS,ERRbadpath)); + + if(sys_stat(fname,&st)!=0) { + DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno))); + return(ERROR(ERRDOS,ERRbadpath)); + } + } + + DEBUG(3,("call_trans2setfilepathinfo(%d) %s info_level=%d totdata=%d\n", + tran_call,fname,info_level,total_data)); + + /* Realloc the parameter and data sizes */ + params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + size = st.st_size; + tvs.modtime = st.st_mtime; + tvs.actime = st.st_atime; + mode = dos_mode(cnum,fname,&st); + + if (total_data > 0 && IVAL(pdata,0) == total_data) { + /* uggh, EAs for OS2 */ + DEBUG(4,("Rejecting EA request with total_data=%d\n",total_data)); + SSVAL(params,0,ERROR_EAS_NOT_SUPPORTED); + + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + + return(-1); + } + + switch (info_level) + { + case 1: + tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess); + tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite); + mode = SVAL(pdata,l1_attrFile); + size = IVAL(pdata,l1_cbFile); + break; + + case 2: + tvs.actime = make_unix_date2(pdata+l1_fdateLastAccess); + tvs.modtime = make_unix_date2(pdata+l1_fdateLastWrite); + mode = SVAL(pdata,l1_attrFile); + size = IVAL(pdata,l1_cbFile); + break; + + case 3: + tvs.actime = make_unix_date2(pdata+8); + tvs.modtime = make_unix_date2(pdata+12); + size = IVAL(pdata,16); + mode = IVAL(pdata,24); + break; + + case 4: + tvs.actime = make_unix_date2(pdata+8); + tvs.modtime = make_unix_date2(pdata+12); + size = IVAL(pdata,16); + mode = IVAL(pdata,24); + break; + + case SMB_SET_FILE_BASIC_INFO: + pdata += 8; /* create time */ + tvs.actime = interpret_long_date(pdata); pdata += 8; + tvs.modtime=MAX(interpret_long_date(pdata),interpret_long_date(pdata+8)); + pdata += 16; + mode = IVAL(pdata,0); + break; + + case SMB_SET_FILE_END_OF_FILE_INFO: + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return(ERROR(ERRDOS,ERRunknownlevel)); + size = IVAL(pdata,0); + break; + + case SMB_SET_FILE_DISPOSITION_INFO: /* not supported yet */ + case SMB_SET_FILE_ALLOCATION_INFO: /* not supported yet */ + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + + if (!tvs.actime) tvs.actime = st.st_atime; + if (!tvs.modtime) tvs.modtime = st.st_mtime; + if (!size) size = st.st_size; + + /* Try and set the times, size and mode of this file - if they are different + from the current values */ + if(st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) { + if(sys_utime(fname, &tvs)!=0) + return(ERROR(ERRDOS,ERRnoaccess)); + } + if(mode != dos_mode(cnum,fname,&st) && dos_chmod(cnum,fname,mode,NULL)) { + DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); + return(ERROR(ERRDOS,ERRnoaccess)); + } + if(size != st.st_size) { + if (fd == -1) { + fd = sys_open(fname,O_RDWR,0); + if (fd == -1) + return(ERROR(ERRDOS,ERRbadpath)); + set_filelen(fd, size); + close(fd); + } else { + set_filelen(fd, size); + } + } + + SSVAL(params,0,0); + + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + + return(-1); +} + +/**************************************************************************** + reply to a TRANS2_MKDIR (make directory with extended attributes). +****************************************************************************/ +static int call_trans2mkdir(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + char *params = *pparams; + pstring directory; + int ret = -1; + + if (!CAN_WRITE(cnum)) + return(ERROR(ERRSRV,ERRaccess)); + + strcpy(directory, ¶ms[4]); + + DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); + + unix_convert(directory,cnum); + if (check_name(directory,cnum)) + ret = sys_mkdir(directory,unix_mode(cnum,aDIR)); + + if(ret < 0) + { + DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno))); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + /* Realloc the parameter and data sizes */ + params = *pparams = Realloc(*pparams,2); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + SSVAL(params,0,0); + + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0); + + return(-1); +} + +/**************************************************************************** + reply to a TRANS2_FINDNOTIFYFIRST (start monitoring a directory for changes) + We don't actually do this - we just send a null response. +****************************************************************************/ +static int call_trans2findnotifyfirst(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + static uint16 fnf_handle = 257; + char *params = *pparams; + uint16 info_level = SVAL(params,4); + + DEBUG(3,("call_trans2findnotifyfirst - info_level %d\n", info_level)); + + switch (info_level) + { + case 1: + case 2: + break; + default: + return(ERROR(ERRDOS,ERRunknownlevel)); + } + + /* Realloc the parameter and data sizes */ + params = *pparams = Realloc(*pparams,6); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + SSVAL(params,0,fnf_handle); + SSVAL(params,2,0); /* No changes */ + SSVAL(params,4,0); /* No EA errors */ + + fnf_handle++; + + if(fnf_handle == 0) + fnf_handle = 257; + + send_trans2_replies(outbuf, bufsize, params, 6, *ppdata, 0); + + return(-1); +} + +/**************************************************************************** + reply to a TRANS2_FINDNOTIFYNEXT (continue monitoring a directory for + changes). Currently this does nothing. +****************************************************************************/ +static int call_trans2findnotifynext(char *inbuf, char *outbuf, int length, int bufsize, + int cnum, char **pparams, char **ppdata) +{ + char *params = *pparams; + + DEBUG(3,("call_trans2findnotifynext\n")); + + /* Realloc the parameter and data sizes */ + params = *pparams = Realloc(*pparams,4); + if(params == NULL) + return(ERROR(ERRDOS,ERRnomem)); + + SSVAL(params,0,0); /* No changes */ + SSVAL(params,2,0); /* No EA errors */ + + send_trans2_replies(outbuf, bufsize, params, 4, *ppdata, 0); + + return(-1); +} + +/**************************************************************************** + reply to a SMBfindclose (stop trans2 directory search) +****************************************************************************/ +int reply_findclose(char *inbuf,char *outbuf,int length,int bufsize) +{ + int cnum; + int outsize = 0; + uint16 dptr_num=SVAL(inbuf,smb_vwv0); + + cnum = SVAL(inbuf,smb_tid); + + DEBUG(3,("reply_findclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + + dptr_close(dptr_num); + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s SMBfindclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); + + return(outsize); +} + +/**************************************************************************** + reply to a SMBfindnclose (stop FINDNOTIFYFIRST directory search) +****************************************************************************/ +int reply_findnclose(char *inbuf,char *outbuf,int length,int bufsize) +{ + int cnum; + int outsize = 0; + int dptr_num= -1; + + cnum = SVAL(inbuf,smb_tid); + dptr_num = SVAL(inbuf,smb_vwv0); + + DEBUG(3,("reply_findnclose, cnum = %d, dptr_num = %d\n", cnum, dptr_num)); + + /* We never give out valid handles for a + findnotifyfirst - so any dptr_num is ok here. + Just ignore it. */ + + outsize = set_message(outbuf,0,0,True); + + DEBUG(3,("%s SMB_findnclose cnum=%d, dptr_num = %d\n",timestring(),cnum,dptr_num)); + + return(outsize); +} + + +/**************************************************************************** + reply to a SMBtranss2 - just ignore it! +****************************************************************************/ +int reply_transs2(char *inbuf,char *outbuf,int length,int bufsize) +{ + DEBUG(4,("Ignoring transs2 of length %d\n",length)); + return(-1); +} + +/**************************************************************************** + reply to a SMBtrans2 +****************************************************************************/ +int reply_trans2(char *inbuf,char *outbuf,int length,int bufsize) +{ + int outsize = 0; + int cnum = SVAL(inbuf,smb_tid); + unsigned int total_params = SVAL(inbuf, smb_tpscnt); + unsigned int total_data =SVAL(inbuf, smb_tdscnt); +#if 0 + unsigned int max_param_reply = SVAL(inbuf, smb_mprcnt); + unsigned int max_data_reply = SVAL(inbuf, smb_mdrcnt); + unsigned int max_setup_fields = SVAL(inbuf, smb_msrcnt); + BOOL close_tid = BITSETW(inbuf+smb_flags,0); + BOOL no_final_response = BITSETW(inbuf+smb_flags,1); + int32 timeout = IVALS(inbuf,smb_timeout); +#endif + unsigned int suwcnt = SVAL(inbuf, smb_suwcnt); + unsigned int tran_call = SVAL(inbuf, smb_setup0); + char *params = NULL, *data = NULL; + int num_params, num_params_sofar, num_data, num_data_sofar; + + outsize = set_message(outbuf,0,0,True); + + /* All trans2 messages we handle have smb_sucnt == 1 - ensure this + is so as a sanity check */ + if(suwcnt != 1 ) + { + DEBUG(2,("Invalid smb_sucnt in trans2 call\n")); + return(ERROR(ERRSRV,ERRerror)); + } + + /* Allocate the space for the maximum needed parameters and data */ + if (total_params > 0) + params = (char *)malloc(total_params); + if (total_data > 0) + data = (char *)malloc(total_data); + + if ((total_params && !params) || (total_data && !data)) + { + DEBUG(2,("Out of memory in reply_trans2\n")); + return(ERROR(ERRDOS,ERRnomem)); + } + + /* Copy the param and data bytes sent with this request into + the params buffer */ + num_params = num_params_sofar = SVAL(inbuf,smb_pscnt); + num_data = num_data_sofar = SVAL(inbuf, smb_dscnt); + + memcpy( params, smb_base(inbuf) + SVAL(inbuf, smb_psoff), num_params); + memcpy( data, smb_base(inbuf) + SVAL(inbuf, smb_dsoff), num_data); + + if(num_data_sofar < total_data || num_params_sofar < total_params) + { + /* We need to send an interim response then receive the rest + of the parameter/data bytes */ + outsize = set_message(outbuf,0,0,True); + send_smb(Client,outbuf); + + while( num_data_sofar < total_data || num_params_sofar < total_params) + { + receive_smb(Client,inbuf, 0); + + /* Ensure this is still a trans2 packet (sanity check) */ + if(CVAL(inbuf, smb_com) != SMBtranss2) + { + outsize = set_message(outbuf,0,0,True); + DEBUG(2,("Invalid secondary trans2 packet\n")); + free(params); + free(data); + return(ERROR(ERRSRV,ERRerror)); + } + + /* Revise total_params and total_data in case they have changed downwards */ + total_params = SVAL(inbuf, smb_tpscnt); + total_data = SVAL(inbuf, smb_tdscnt); + num_params_sofar += (num_params = SVAL(inbuf,smb_spscnt)); + num_data_sofar += ( num_data = SVAL(inbuf, smb_sdscnt)); + memcpy( ¶ms[ SVAL(inbuf, smb_spsdisp)], + smb_base(inbuf) + SVAL(inbuf, smb_spsoff), num_params); + memcpy( &data[SVAL(inbuf, smb_sdsdisp)], + smb_base(inbuf)+ SVAL(inbuf, smb_sdsoff), num_data); + } + } + + if (Protocol >= PROTOCOL_NT1) { + uint16 flg2 = SVAL(outbuf,smb_flg2); + SSVAL(outbuf,smb_flg2,flg2 | 0x40); /* IS_LONG_NAME */ + } + + /* Now we must call the relevant TRANS2 function */ + switch(tran_call) + { + case TRANSACT2_OPEN: + outsize = call_trans2open(inbuf, outbuf, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_FINDFIRST: + outsize = call_trans2findfirst(inbuf, outbuf, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_FINDNEXT: + outsize = call_trans2findnext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_QFSINFO: + outsize = call_trans2qfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_SETFSINFO: + outsize = call_trans2setfsinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_QPATHINFO: + case TRANSACT2_QFILEINFO: + outsize = call_trans2qfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); + break; + case TRANSACT2_SETPATHINFO: + case TRANSACT2_SETFILEINFO: + outsize = call_trans2setfilepathinfo(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data, total_data); + break; + case TRANSACT2_FINDNOTIFYFIRST: + outsize = call_trans2findnotifyfirst(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_FINDNOTIFYNEXT: + outsize = call_trans2findnotifynext(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + case TRANSACT2_MKDIR: + outsize = call_trans2mkdir(inbuf, outbuf, length, bufsize, cnum, ¶ms, &data); + break; + default: + /* Error in request */ + DEBUG(2,("%s Unknown request %d in trans2 call\n",timestring(), tran_call)); + if(params) + free(params); + if(data) + free(data); + return (ERROR(ERRSRV,ERRerror)); + } + + /* As we do not know how many data packets will need to be + returned here the various call_trans2xxxx calls + must send their own. Thus a call_trans2xxx routine only + returns a value other than -1 when it wants to send + an error packet. + */ + + if(params) + free(params); + if(data) + free(data); + return outsize; /* If a correct response was needed the call_trans2xxx + calls have already sent it. If outsize != -1 then it is + returning an error packet. */ +} diff --git a/source3/smbd/vt_mode.c b/source3/smbd/vt_mode.c new file mode 100644 index 0000000000..83b62a38ac --- /dev/null +++ b/source3/smbd/vt_mode.c @@ -0,0 +1,496 @@ +/* vt_mode.c */ +/* +support vtp-sessions + +written by Christian A. Lademann <cal@zls.com> +*/ + +/* +02.05.95:cal:ported to samba-1.9.13 +*/ + +#define __vt_mode_c__ + + +/* #include <stdio.h> */ +/* #include <fcntl.h> */ +/* #include <sys/types.h> */ +/* #include <unistd.h> */ +/* #include <signal.h> */ +/* #include <errno.h> */ +/* #include <ctype.h> */ +/* #include <utmp.h> */ +/* #include <sys/param.h> */ +/* #include <sys/ioctl.h> */ +/* #include <stdlib.h> */ +/* #include <string.h> */ + +#include "includes.h" +#include "vt_mode.h" +#include <utmp.h> + +#ifdef SCO + extern char *strdup(); +#endif + +extern int Client; + +#ifdef LINUX +# define HAS_VTY +#endif + +#ifdef SCO +# define HAS_PTY +# define HAS_VTY + +# include <sys/tty.h> +#endif + +extern int DEBUGLEVEL; +extern char *InBuffer, *OutBuffer; +extern int done_become_user; + +char master_name [64], slave_name [64]; +int master, slave, i, o, e; + +int ms_type = MS_NONE, + ms_poll = 0; + + +/* +VT_Check: test incoming packet for "vtp" or "iVT1\0" +*/ +int VT_Check(buffer) +char *buffer; +{ + DEBUG(3,("Checking packet: <%10s...>\n", buffer+4)); + if((strncmp(buffer+4, "vtp", 3) == 0 && smb_len(buffer) == 3) || (strncmp(buffer+4, "iVT1\0", 5) == 0 && smb_len(buffer) == 5)) + return(1); + else + return(0); +} + + +/* +VT_Start_utmp: prepare /etc/utmp for /bin/login +*/ +VT_Start_utmp() +{ + struct utmp u, *v; + char *tt; + + + setutent(); + + strcpy(u.ut_line, VT_Line); + + if((v = getutline(&u)) == NULL) { + if(strncmp(VT_Line, "tty", 3) == 0) + tt = VT_Line + 3; + else if(strlen(VT_Line) > 4) + tt = VT_Line + strlen(VT_Line) - 4; + else + tt = VT_Line; + + strcpy(u.ut_id, tt); + u.ut_time = time((time_t*)0); + } + + strcpy(u.ut_user, "LOGIN"); + strcpy(u.ut_line, VT_Line); + u.ut_pid = getpid(); + u.ut_type = LOGIN_PROCESS; + pututline(&u); + + endutent(); + + return(0); +} + + +/* +VT_Stop_utmp: prepare /etc/utmp for other processes +*/ +VT_Stop_utmp() +{ + struct utmp u, *v; + + + if(VT_Line != NULL) { + setutent(); + + strcpy(u.ut_line, VT_Line); + + if((v = getutline(&u)) != NULL) { + strcpy(v->ut_user, ""); + v->ut_type = DEAD_PROCESS; + v->ut_time = time((time_t*)0); + pututline(v); + } + + endutent(); + } + + return(0); +} + + +/* +VT_AtExit: Things to do when the program exits +*/ +void VT_AtExit() +{ + if(VT_ChildPID > 0) { + kill(VT_ChildPID, SIGHUP); + (void)wait(NULL); + } + + VT_Stop_utmp(); +} + + +/* +VT_SigCLD: signalhandler for SIGCLD: set flag if child-process died +*/ +void VT_SigCLD(sig) +int sig; +{ + if(wait(NULL) == VT_ChildPID) + VT_ChildDied = True; + else + signal(SIGCLD, VT_SigCLD); +} + + +/* +VT_SigEXIT: signalhandler for signals that cause the process to exit +*/ +void VT_SigEXIT(sig) +int sig; +{ + VT_AtExit(); + + exit(1); +} + + +/* +VT_Start: initialize vt-specific data, alloc pty, spawn shell and send ACK +*/ +int VT_Start() +{ + char OutBuf [64], *X, *Y; + + + ms_type = MS_NONE; + master = slave = -1; + +#ifdef HAS_VTY +#ifdef LINUX +# define MASTER_TMPL "/dev/pty " +# define SLAVE_TMPL "/dev/tty " +# define LETTER1 "pqrs" +# define POS1 8 +# define LETTER2 "0123456789abcdef" +# define POS2 9 +#endif + +#ifdef SCO +# define MASTER_TMPL "/dev/ptyp_ " +# define SLAVE_TMPL "/dev/ttyp_ " +# define LETTER1 "0123456" +# define POS1 10 +# define LETTER2 "0123456789abcdef" +# define POS2 11 +#endif + + if(ms_poll == MS_VTY || ms_poll == 0) { + strcpy(master_name, MASTER_TMPL); + strcpy(slave_name, SLAVE_TMPL); + + for(X = LETTER1; *X && master < 0; X++) + for(Y = LETTER2; *Y && master < 0; Y++) { + master_name [POS1] = *X; + master_name [POS2] = *Y; + if((master = open(master_name, O_RDWR)) >= 0) { + slave_name [POS1] = *X; + slave_name [POS2] = *Y; + if((slave = open(slave_name, O_RDWR)) < 0) + close(master); + } + } + + if(master >= 0 && slave >= 0) + ms_type = MS_VTY; + } + +# undef MASTER_TMPL +# undef SLAVE_TMPL +# undef LETTER1 +# undef LETTER2 +# undef POS1 +# undef POS2 +#endif + + +#ifdef HAS_PTY +#ifdef SCO +# define MASTER_TMPL "/dev/ptyp%d" +# define SLAVE_TMPL "/dev/ttyp%d" +# define MIN_I 0 +# define MAX_I 63 +#endif + + if(ms_poll == MS_PTY || ms_poll == 0) { + int i; + + for(i = MIN_I; i <= MAX_I && master < 0; i++) { + sprintf(master_name, MASTER_TMPL, i); + if((master = open(master_name, O_RDWR)) >= 0) { + sprintf(slave_name, SLAVE_TMPL, i); + if((slave = open(slave_name, O_RDWR)) < 0) + close(master); + } + } + + if(master >= 0 && slave >= 0) + ms_type = MS_PTY; + } + +# undef MASTER_TMPL +# undef SLAVE_TMPL +# undef MIN_I +# undef MAX_I +#endif + + + if(! ms_type) + return(-1); + + VT_Line = strdup(strrchr(slave_name, '/') + 1); + + switch((VT_ChildPID = fork())) { + case -1: + return(-1); + break; + + case 0: +#ifdef SCO + setsid(); +#endif + close(0); + close(1); + close(2); + + i = open(slave_name, O_RDWR); + o = open(slave_name, O_RDWR); + e = open(slave_name, O_RDWR); + +#ifdef LINUX + setsid(); + if (ioctl(slave, TIOCSCTTY, (char *)NULL) == -1) + exit(1); +#endif +#ifdef SCO + tcsetpgrp(0, getpid()); +#endif + + VT_Start_utmp(); + + system("stty sane"); + execlp("/bin/login", "login", "-c", (char*)0); + exit(1); + break; + + default: + VT_Mode = True; + VT_Status = VT_OPEN; + VT_ChildDied = False; + VT_Fd = master; + + signal(SIGCLD, VT_SigCLD); + + signal(SIGHUP, VT_SigEXIT); + signal(SIGTERM, VT_SigEXIT); + signal(SIGINT, VT_SigEXIT); + signal(SIGQUIT, VT_SigEXIT); + + memset(OutBuf, 0, sizeof(OutBuf)); + OutBuf [4] = 0x06; + _smb_setlen(OutBuf, 1); + + send_smb(Client,OutBuf); + + return(0); + break; + } +} + + +/* +VT_Output: transport data from socket to pty +*/ +int VT_Output(Buffer) +char *Buffer; +{ + int i, len, nb; + + + if(VT_Status != VT_OPEN) + return(-1); + + len = smb_len(Buffer); + + nb = write(VT_Fd, Buffer + 4, len); + + return((nb == len) ? 0 : -1); +} + + +/* +VT_Input: transport data from pty to socket +*/ +int VT_Input(Buffer, Size) +char *Buffer; +int Size; +{ + int len; + + + if(VT_Status != VT_OPEN) + return(-1); + + memset(Buffer, 0, Size); + len = read(VT_Fd, Buffer + 4, MIN(VT_MAXREAD, Size)); + + _smb_setlen(Buffer, len); + + return(len + 4); +} + + +/* +VT_Process: main loop while in vt-mode +*/ +void VT_Process() +{ + static int trans_num = 0; + extern int Client; + int nread; + + + VT_Start(); + + atexit(VT_AtExit); + + while (True) { + int32 len; + int msg_type; + int msg_flags; + int counter; + int last_keepalive=0; + struct fd_set si; + struct timeval to, *top; + int n, ret, t; + + + errno = 0; + t = SMBD_SELECT_LOOP*1000; + + + FD_ZERO(&si); + FD_SET(Client, &si); + + FD_SET(VT_Fd, &si); + + if(t >= 0) { + to.tv_sec = t / 1000; + to.tv_usec = t - (to.tv_sec * 1000); + + top = &to; + } else + top = NULL; + + if(VT_ChildDied) + goto leave_VT_Process; + + n = select(MAX(VT_Fd, Client) + 1, &si, NULL, NULL, top); + + if(VT_ChildDied) + goto leave_VT_Process; + + if(n == 0) { + int i; + time_t t; + BOOL allidle = True; + extern int keepalive; + + counter += SMBD_SELECT_LOOP; + + t = time(NULL); + + if (keepalive && (counter-last_keepalive)>keepalive) { + if (!send_keepalive(Client)) + goto leave_VT_Process; + last_keepalive = counter; + } + } else if(n > 0) { + counter = 0; + + if(FD_ISSET(VT_Fd, &si)) { + /* got input from vt */ + nread = VT_Input(OutBuffer, MIN(BUFFER_SIZE,lp_maxxmit())); + + if(nread > 0) + send_smb(Client,OutBuffer); + } + + if(FD_ISSET(Client, &si)) { + /* got input from socket */ + + if(receive_smb(Client,InBuffer, 0)) { + msg_type = CVAL(InBuffer,0); + msg_flags = CVAL(InBuffer,1); + + len = smb_len(InBuffer); + + DEBUG(6,("got message type 0x%x of len 0x%x\n",msg_type,len)); + + nread = len + 4; + + DEBUG(3,("%s Transaction %d of length %d\n",timestring(),trans_num,nread)); + + if(msg_type == 0) + VT_Output(InBuffer); + else { + nread = construct_reply(InBuffer,OutBuffer,nread,MIN(BUFFER_SIZE,lp_maxxmit())); + + if(nread > 0) { + if (nread != smb_len(OutBuffer) + 4) { + DEBUG(0,("ERROR: Invalid message response size! %d %d\n", + nread, + smb_len(OutBuffer))); + } else + send_smb(Client,OutBuffer); + } + } + } else + if(errno == EBADF) + goto leave_VT_Process; + } + } + + trans_num++; + } + + leave_VT_Process: +/* + if(VT_ChildPID > 0) + kill(VT_ChildPID, SIGHUP); + + VT_Stop_utmp(VT_Line); + return; +*/ + close_sockets(); + exit(0); +} diff --git a/source3/utils/nmblookup.c b/source3/utils/nmblookup.c new file mode 100644 index 0000000000..aa43173332 --- /dev/null +++ b/source3/utils/nmblookup.c @@ -0,0 +1,217 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + NBT client - used to lookup netbios names + Copyright (C) Andrew Tridgell 1994-1995 + + 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. + +*/ + +#ifdef SYSLOG +#undef SYSLOG +#endif + +#include "includes.h" +#include "nameserv.h" + +extern int DEBUGLEVEL; + +extern pstring scope; + +extern struct in_addr bcast_ip; +extern pstring myhostname; + +static BOOL got_bcast = False; + +int ServerFD= -1; + +/**************************************************************************** + open the socket communication + **************************************************************************/ +static BOOL open_sockets(void) +{ + struct hostent *hp; + + /* get host info */ + if ((hp = Get_Hostbyname(myhostname)) == 0) + { + DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",myhostname)); + return False; + } + + ServerFD = open_socket_in(SOCK_DGRAM, 0,3); + + if (ServerFD == -1) + return(False); + + set_socket_options(ServerFD,"SO_BROADCAST"); + + DEBUG(3, ("Socket opened.\n")); + return True; +} + + +/**************************************************************************** + initialise connect, service and file structs +****************************************************************************/ +static BOOL init_structs(void ) +{ + struct in_addr myip; + + if (!get_myname(myhostname,&myip)) + return(False); + + /* Read the broadcast address from the interface */ + { + struct in_addr ip0,ip2; + + ip0 = myip; + + if (!got_bcast) { + get_broadcast(&ip0,&bcast_ip,&ip2); + + DEBUG(2,("Using broadcast %s\n",inet_ntoa(bcast_ip))); + } + } + + return True; +} + +/**************************************************************************** +usage on the program +****************************************************************************/ +static void usage(void) +{ + printf("Usage: nmblookup [-M] [-B bcast address] [-d debuglevel] name\n"); + printf("Version %s\n",VERSION); + printf("\t-d debuglevel set the debuglevel\n"); + printf("\t-B broadcast address the address to use for broadcasts\n"); + printf("\t-M searches for a master browser\n"); + printf("\t-S lookup node status as well\n"); + printf("\n"); +} + + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc,char *argv[]) +{ + int opt; + unsigned int lookup_type = 0x20; + pstring lookup; + extern int optind; + extern char *optarg; + BOOL find_master=False; + BOOL find_status=False; + int i; + + DEBUGLEVEL = 1; + *lookup = 0; + + TimeInit(); + + setup_logging(argv[0],True); + + charset_initialise(); + + while ((opt = getopt(argc, argv, "p:d:B:i:SMh")) != EOF) + switch (opt) + { + case 'B': + { + unsigned long a = interpret_addr(optarg); + putip((char *)&bcast_ip,(char *)&a); + got_bcast = True; + } + break; + case 'i': + strcpy(scope,optarg); + strupper(scope); + break; + case 'M': + find_master = True; + break; + case 'S': + find_status = True; + break; + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + case 'h': + usage(); + exit(0); + break; + default: + usage(); + exit(1); + } + + if (argc < 2) { + usage(); + exit(1); + } + + init_structs(); + if (!open_sockets()) return(1); + + DEBUG(1,("Sending queries to %s\n",inet_ntoa(bcast_ip))); + + + for (i=optind;i<argc;i++) + { + BOOL bcast = True; + int retries = 2; + char *p; + struct in_addr ip; + + strcpy(lookup,argv[i]); + + if (find_master) { + if (*lookup == '-') { + strcpy(lookup,"\01\02__MSBROWSE__\02"); + lookup_type = 1; + } else { + lookup_type = 0x1d; + } + } + + p = strchr(lookup,'#'); + + if (p) { + *p = 0; + sscanf(p+1,"%x",&lookup_type); + bcast = False; + retries = 1; + } + + if (name_query(ServerFD,lookup,lookup_type,bcast,True, + bcast_ip,&ip,NULL)) + { + printf("%s %s\n",inet_ntoa(ip),lookup); + if (find_status) + { + printf("Looking up status of %s\n",inet_ntoa(ip)); + name_status(ServerFD,lookup,lookup_type,True,ip,NULL,NULL,NULL); + printf("\n"); + } + } else { + printf("couldn't find name %s\n",lookup); + } + } + + return(0); +} diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c new file mode 100644 index 0000000000..167eb2ed5f --- /dev/null +++ b/source3/utils/smbpasswd.c @@ -0,0 +1,456 @@ +#ifdef SMB_PASSWD + +/* + * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright + * (C) Jeremy Allison 1995. + * + * 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. + */ + +#include "includes.h" +#include "des.h" + +/* Static buffers we will return. */ +static struct smb_passwd pw_buf; +static pstring user_name; +static unsigned char smbpwd[16]; +static unsigned char smbntpwd[16]; + +static int gethexpwd(char *p, char *pwd) +{ + int i; + unsigned char lonybble, hinybble; + char *hexchars = "0123456789ABCDEF"; + char *p1, *p2; + for (i = 0; i < 32; i += 2) { + hinybble = toupper(p[i]); + lonybble = toupper(p[i + 1]); + + p1 = strchr(hexchars, hinybble); + p2 = strchr(hexchars, lonybble); + if (!p1 || !p2) + return (False); + + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + pwd[i / 2] = (hinybble << 4) | lonybble; + } + return (True); +} + +struct smb_passwd * +_my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, + BOOL *got_valid_nt_entry, long *pwd_seekpos) +{ + char linebuf[256]; + unsigned char c; + unsigned char *p; + long uidval; + long linebuf_len; + + /* + * Scan the file, a line at a time and check if the name matches. + */ + while (!feof(fp)) { + linebuf[0] = '\0'; + *pwd_seekpos = ftell(fp); + + fgets(linebuf, 256, fp); + if (ferror(fp)) + return NULL; + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + linebuf_len = strlen(linebuf); + if (linebuf[linebuf_len - 1] != '\n') { + c = '\0'; + while (!ferror(fp) && !feof(fp)) { + c = fgetc(fp); + if (c == '\n') + break; + } + } else + linebuf[linebuf_len - 1] = '\0'; + + if ((linebuf[0] == 0) && feof(fp)) + break; + /* + * The line we have should be of the form :- + * + * username:uid:[32hex bytes]:....other flags presently + * ignored.... + * + * or, + * + * username:uid:[32hex bytes]:[32hex bytes]:....ignored.... + * + * if Windows NT compatible passwords are also present. + */ + + if (linebuf[0] == '#' || linebuf[0] == '\0') + continue; + p = (unsigned char *) strchr(linebuf, ':'); + if (p == NULL) + continue; + /* + * As 256 is shorter than a pstring we don't need to check + * length here - if this ever changes.... + */ + strncpy(user_name, linebuf, PTR_DIFF(p, linebuf)); + user_name[PTR_DIFF(p, linebuf)] = '\0'; + if (!strequal(user_name, name)) + continue; + + /* User name matches - get uid and password */ + p++; /* Go past ':' */ + if (!isdigit(*p)) + return (False); + + uidval = atoi((char *) p); + while (*p && isdigit(*p)) + p++; + + if (*p != ':') + return (False); + + /* + * Now get the password value - this should be 32 hex digits + * which are the ascii representations of a 16 byte string. + * Get two at a time and put them into the password. + */ + p++; + *pwd_seekpos += PTR_DIFF(p, linebuf); /* Save exact position + * of passwd in file - + * this is used by + * smbpasswd.c */ + if (*p == '*' || *p == 'X') { + /* Password deliberately invalid - end here. */ + *valid_old_pwd = False; + *got_valid_nt_entry = False; + pw_buf.smb_nt_passwd = NULL; /* No NT password (yet)*/ + + /* Now check if the NT compatible password is + available. */ + p += 33; /* Move to the first character of the line after + the lanman password. */ + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + /* NT Entry was valid - even if 'X' or '*', can be overwritten */ + *got_valid_nt_entry = True; + if (*p != '*' && *p != 'X') { + if(gethexpwd(p,smbntpwd)) + pw_buf.smb_nt_passwd = smbntpwd; + } + } + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + pw_buf.smb_passwd = NULL; /* No password */ + return (&pw_buf); + } + if (linebuf_len < (PTR_DIFF(p, linebuf) + 33)) + return (False); + + if (p[32] != ':') + return (False); + + if (!strncasecmp(p, "NO PASSWORD", 11)) { + pw_buf.smb_passwd = NULL; /* No password */ + } else { + if(!gethexpwd(p,smbpwd)) + return False; + pw_buf.smb_passwd = smbpwd; + } + + pw_buf.smb_name = user_name; + pw_buf.smb_userid = uidval; + pw_buf.smb_nt_passwd = NULL; + *got_valid_nt_entry = False; + *valid_old_pwd = True; + + /* Now check if the NT compatible password is + available. */ + p += 33; /* Move to the first character of the line after + the lanman password. */ + if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) { + /* NT Entry was valid - even if 'X' or '*', can be overwritten */ + *got_valid_nt_entry = True; + if (*p != '*' && *p != 'X') { + if(gethexpwd(p,smbntpwd)) + pw_buf.smb_nt_passwd = smbntpwd; + } + } + return &pw_buf; + } + return NULL; +} + +/* + * Print command usage on stderr and die. + */ +void +usage(char *name) +{ + fprintf(stderr, "Usage is : %s [username]\n", name); + exit(1); +} + +int main(int argc, char **argv) +{ + int real_uid; + struct passwd *pwd; + fstring old_passwd; + uchar old_p16[16]; + uchar old_nt_p16[16]; + fstring new_passwd; + uchar new_p16[16]; + uchar new_nt_p16[16]; + char *p; + struct smb_passwd *smb_pwent; + FILE *fp; + BOOL valid_old_pwd = False; + BOOL got_valid_nt_entry = False; + long seekpos; + int pwfd; + char ascii_p16[66]; + char c; + int ret, i, err, writelen; + int lockfd = -1; + char *pfile = SMB_PASSWD_FILE; + char readbuf[16 * 1024]; + + setup_logging(argv[0],True); + + charset_initialise(); + +#ifndef DEBUG_PASSWORD + /* Check the effective uid */ + if (geteuid() != 0) { + fprintf(stderr, "%s: Must be setuid root.\n", argv[0]); + exit(1); + } +#endif + + /* Get the real uid */ + real_uid = getuid(); + + /* Deal with usage problems */ + if (real_uid == 0) { + /* As root we can change anothers password. */ + if (argc != 1 && argc != 2) + usage(argv[0]); + } else if (argc != 1) + usage(argv[0]); + + + if (real_uid == 0 && argc == 2) { + /* If we are root we can change anothers password. */ + strncpy(user_name, argv[1], sizeof(user_name) - 1); + user_name[sizeof(user_name) - 1] = '\0'; + pwd = getpwnam(user_name); + } else { + pwd = getpwuid(real_uid); + } + + if (pwd == 0) { + fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]); + exit(1); + } + /* If we are root we don't ask for the old password. */ + old_passwd[0] = '\0'; + if (real_uid != 0) { + p = getpass("Old SMB password:"); + strncpy(old_passwd, p, sizeof(fstring)); + old_passwd[sizeof(fstring)-1] = '\0'; + } + new_passwd[0] = '\0'; + p = getpass("New SMB password:"); + strncpy(new_passwd, p, sizeof(fstring)); + new_passwd[sizeof(fstring)-1] = '\0'; + p = getpass("Retype new SMB password:"); + if (strcmp(p, new_passwd)) { + fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]); + exit(1); + } + + if (new_passwd[0] == '\0') { + printf("Password not set\n"); + exit(0); + } + + /* Calculate the MD4 hash (NT compatible) of the old and new passwords */ + memset(old_nt_p16, '\0', 16); + E_md4hash((uchar *)old_passwd, old_nt_p16); + + memset(new_nt_p16, '\0', 16); + E_md4hash((uchar *) new_passwd, new_nt_p16); + + /* Mangle the passwords into Lanman format */ + old_passwd[14] = '\0'; + strupper(old_passwd); + new_passwd[14] = '\0'; + strupper(new_passwd); + + /* + * Calculate the SMB (lanman) hash functions of both old and new passwords. + */ + + memset(old_p16, '\0', 16); + E_P16((uchar *) old_passwd, old_p16); + + memset(new_p16, '\0', 16); + E_P16((uchar *) new_passwd, new_p16); + + /* + * Open the smbpaswd file XXXX - we need to parse smb.conf to get the + * filename + */ + if ((fp = fopen(pfile, "r+")) == NULL) { + err = errno; + fprintf(stderr, "%s: Failed to open password file %s.\n", + argv[0], pfile); + errno = err; + perror(argv[0]); + exit(err); + } + /* Set read buffer to 16k for effiecient reads */ + setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf)); + + /* make sure it is only rw by the owner */ + chmod(pfile, 0600); + + /* Lock the smbpasswd file for write. */ + if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) { + err = errno; + fprintf(stderr, "%s: Failed to lock password file %s.\n", + argv[0], pfile); + fclose(fp); + errno = err; + perror(argv[0]); + exit(err); + } + /* Get the smb passwd entry for this user */ + smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd, + &got_valid_nt_entry, &seekpos); + if (smb_pwent == NULL) { + fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n", + argv[0], pwd->pw_name, pfile); + fclose(fp); + pw_file_unlock(lockfd); + exit(1); + } + /* If we are root we don't need to check the old password. */ + if (real_uid != 0) { + if ((valid_old_pwd == False) || (smb_pwent->smb_passwd == NULL)) { + fprintf(stderr, "%s: User %s is disabled, plase contact your administrator to enable it.\n", argv[0], pwd->pw_name); + fclose(fp); + pw_file_unlock(lockfd); + exit(1); + } + /* Check the old Lanman password */ + if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) { + fprintf(stderr, "%s: Couldn't change password.\n", argv[0]); + fclose(fp); + pw_file_unlock(lockfd); + exit(1); + } + /* Check the NT password if it exists */ + if (smb_pwent->smb_nt_passwd != NULL) { + if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) { + fprintf(stderr, "%s: Couldn't change password.\n", argv[0]); + fclose(fp); + pw_file_unlock(lockfd); + exit(1); + } + } + } + /* + * If we get here either we were root or the old password checked out + * ok. + */ + /* Create the 32 byte representation of the new p16 */ + for (i = 0; i < 16; i++) { + sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]); + } + if(got_valid_nt_entry) { + /* Add on the NT md4 hash */ + ascii_p16[32] = ':'; + for (i = 0; i < 16; i++) { + sprintf(&ascii_p16[(i * 2)+33], "%02X", (uchar) new_nt_p16[i]); + } + } + /* + * Do an atomic write into the file at the position defined by + * seekpos. + */ + pwfd = fileno(fp); + ret = lseek(pwfd, seekpos - 1, SEEK_SET); + if (ret != seekpos - 1) { + err = errno; + fprintf(stderr, "%s: seek fail on file %s.\n", + argv[0], pfile); + fclose(fp); + errno = err; + perror(argv[0]); + pw_file_unlock(lockfd); + exit(1); + } + /* Sanity check - ensure the character is a ':' */ + if (read(pwfd, &c, 1) != 1) { + err = errno; + fprintf(stderr, "%s: read fail on file %s.\n", + argv[0], pfile); + fclose(fp); + errno = err; + perror(argv[0]); + pw_file_unlock(lockfd); + exit(1); + } + if (c != ':') { + fprintf(stderr, "%s: sanity check on passwd file %s failed.\n", + argv[0], pfile); + fclose(fp); + pw_file_unlock(lockfd); + exit(1); + } + writelen = (got_valid_nt_entry) ? 65 : 32; + if (write(pwfd, ascii_p16, writelen) != writelen) { + err = errno; + fprintf(stderr, "%s: write fail in file %s.\n", + argv[0], pfile); + fclose(fp); + errno = err; + perror(argv[0]); + pw_file_unlock(lockfd); + exit(err); + } + fclose(fp); + pw_file_unlock(lockfd); + printf("Password changed\n"); + return 0; +} + +#else + +#include "includes.h" + +int +main(int argc, char **argv) +{ + printf("smb password encryption not selected in Makefile\n"); + return 0; +} +#endif diff --git a/source3/utils/status.c b/source3/utils/status.c new file mode 100644 index 0000000000..ed0ae53211 --- /dev/null +++ b/source3/utils/status.c @@ -0,0 +1,258 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + status reporting + Copyright (C) Andrew Tridgell 1994-1995 + + 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. +*/ + +/* + * This program reports current SMB connections + */ + +#ifdef SYSLOG +#undef SYSLOG +#endif + +#include "includes.h" +#include "loadparm.h" + +struct connect_record crec; +extern int DEBUGLEVEL; +extern FILE *dbf; + +static pstring Ucrit_username = ""; /* added by OH */ +int Ucrit_pid[100]; /* Ugly !!! */ /* added by OH */ +int Ucrit_MaxPid=0; /* added by OH */ +unsigned int Ucrit_IsActive = 0; /* added by OH */ +void Ucrit_addUsername(pstring username); /* added by OH */ +unsigned int Ucrit_checkUsername(pstring username); /* added by OH */ +void Ucrit_addPid(int pid); /* added by OH */ +unsigned int Ucrit_checkPid(int pid); /* added by OH */ + +int main(int argc, char *argv[]) +{ + FILE *f; + pstring fname; + int uid, c, n; + static pstring servicesf = CONFIGFILE; + extern char *optarg; + int verbose = 0; + void *dir; + char *s; + BOOL firstopen=True; + BOOL processes_only=False; + int last_pid=0; + + setup_logging(argv[0],True); + + charset_initialise(); + + DEBUGLEVEL = 0; + dbf = fopen("/dev/null","w"); + + if (getuid() != geteuid()) { + printf("smbstatus should not be run setuid\n"); + return(1); + } + + while ((c = getopt(argc, argv, "pdsu:")) != EOF) { + switch (c) { + case 'd': + verbose = 1; + break; + case 'p': + processes_only = 1; + break; + case 's': + strcpy(servicesf, optarg); + break; + case 'u': /* added by OH */ + Ucrit_addUsername(optarg); /* added by OH */ + break; + default: + fprintf(stderr, "Usage: %s [-d] [-p] [-s configfile] [-u username]\n", *argv); /* changed by OH */ + return (-1); + } + } + + + + if (!lp_load(servicesf,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", servicesf); + return (-1); + } + + if (verbose) { + printf("using configfile = %s\n", servicesf); + printf("lockdir = %s\n", *lp_lockdir() ? lp_lockdir() : "NULL"); + } + + strcpy(fname,lp_lockdir()); + standard_sub_basic(fname); + trim_string(fname,"","/"); + strcat(fname,"/STATUS..LCK"); + + f = fopen(fname,"r"); + if (!f) { + printf("Couldn't open status file %s\n",fname); + if (!lp_status(-1)) + printf("You need to have status=yes in your smb config file\n"); + return(0); + } + + uid = getuid(); + + if (!processes_only) { + printf("\nSamba version %s\n",VERSION); + + printf("Service uid gid pid machine\n"); + printf("----------------------------------------------\n"); + } + + while (!feof(f)) + { + if (fread(&crec,sizeof(crec),1,f) != 1) + break; + if ( crec.magic == 0x280267 && process_exists(crec.pid) + && Ucrit_checkUsername(uidtoname(crec.uid)) /* added by OH */ + ) + { + Ucrit_addPid(crec.pid); /* added by OH */ + if (processes_only) { + if (last_pid != crec.pid) + printf("%d\n",crec.pid); + last_pid = crec.pid; /* XXXX we can still get repeats, have to + add a sort at some time */ + } + else + printf("%-10.10s %-8s %-8s %5d %-8s (%s) %s", + crec.name,uidtoname(crec.uid),gidtoname(crec.gid),crec.pid, + crec.machine,crec.addr, + asctime(LocalTime(&crec.start,GMT_TO_LOCAL))); + } + } + fclose(f); + + if (processes_only) exit(0); + + printf("\n"); + + dir = opendir(lp_lockdir()); + if (!dir) return(0); + while ((s=readdirname(dir))) { + char buf[16]; + int pid,mode; + time_t t; + int fd; + pstring lname; + int dev,inode; + + if (sscanf(s,"share.%d.%d",&dev,&inode)!=2) continue; + + strcpy(lname,lp_lockdir()); + trim_string(lname,NULL,"/"); + strcat(lname,"/"); + strcat(lname,s); + + fd = open(lname,O_RDONLY,0); + if (fd < 0) continue; + if (read(fd,buf,16) != 16) continue; + n = read(fd,fname,sizeof(fname)); + fname[MAX(n,0)]=0; + close(fd); + + t = IVAL(buf,0); + mode = IVAL(buf,4); + pid = IVAL(buf,8); + + if ( !Ucrit_checkPid(pid) ) /* added by OH */ + continue; + + if (IVAL(buf,12) != LOCKING_VERSION || !process_exists(pid)) { + if (unlink(lname)==0) + printf("Deleted stale share file %s\n",s); + continue; + } + + fname[sizeof(fname)-1] = 0; + + if (firstopen) { + firstopen=False; + printf("Locked files:\n"); + printf("Pid DenyMode R/W Name\n"); + printf("------------------------------\n"); + } + + + printf("%-5d ",pid); + switch ((mode>>4)&0xF) + { + case DENY_NONE: printf("DENY_NONE "); break; + case DENY_ALL: printf("DENY_ALL "); break; + case DENY_DOS: printf("DENY_DOS "); break; + case DENY_READ: printf("DENY_READ "); break; + case DENY_WRITE:printf("DENY_WRITE "); break; + } + switch (mode&0xF) + { + case 0: printf("RDONLY "); break; + case 1: printf("WRONLY "); break; + case 2: printf("RDWR "); break; + } + printf(" %s %s",fname,asctime(LocalTime(&t,GMT_TO_LOCAL))); + } + closedir(dir); + + if (firstopen) + printf("No locked files\n"); + + return (0); +} + +/* added by OH */ +void Ucrit_addUsername(pstring username) +{ + strcpy(Ucrit_username, username); + if(strlen(Ucrit_username) > 0) + Ucrit_IsActive = 1; +} + +unsigned int Ucrit_checkUsername(pstring username) +{ + if ( !Ucrit_IsActive) return 1; + if (strcmp(Ucrit_username,username) ==0) return 1; + return 0; +} + +void Ucrit_addPid(int pid) +{ + int i; + if ( !Ucrit_IsActive) return; + for (i=0;i<Ucrit_MaxPid;i++) + if( pid == Ucrit_pid[i] ) return; + Ucrit_pid[Ucrit_MaxPid++] = pid; +} + +unsigned int Ucrit_checkPid(int pid) +{ + int i; + if ( !Ucrit_IsActive) return 1; + for (i=0;i<Ucrit_MaxPid;i++) + if( pid == Ucrit_pid[i] ) return 1; + return 0; +} + diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c new file mode 100644 index 0000000000..e1f070a4b8 --- /dev/null +++ b/source3/utils/testparm.c @@ -0,0 +1,113 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + Test validity of smb.conf + Copyright (C) Karl Auer 1993, 1994 + + Extensively modified by Andrew Tridgell, 1995 + + 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. +*/ + +/* + * Testbed for loadparm.c/params.c + * + * This module simply loads a specified configuration file and + * if successful, dumps it's contents to stdout. Note that the + * operation is performed with DEBUGLEVEL at 3. + * + * Useful for a quick 'syntax check' of a configuration file. + * + */ + +#include "includes.h" +#include "smb.h" +#include "params.h" +#include "loadparm.h" + +/* these live in util.c */ +extern FILE *dbf; +extern int DEBUGLEVEL; + +int main(int argc, char *argv[]) +{ + pstring configfile; + int s; + + setup_logging(argv[0],True); + + charset_initialise(); + + if (argc < 2) + strcpy(configfile,CONFIGFILE); + else + strcpy(configfile,argv[1]); + + dbf = stdout; + DEBUGLEVEL = 2; + + printf("Load smb config files from %s\n",configfile); + + if (!lp_load(configfile,False)) + { + printf("Error loading services.\n"); + return(1); + } + + + printf("Loaded services file OK.\n"); + + for (s=0;s<1000;s++) + if (VALID_SNUM(s)) + if (strlen(lp_servicename(s)) > 8) { + printf("WARNING: You have some share names that are longer than 8 chars\n"); + printf("These may give errors while browsing or may not be accessible\nto some older clients\n"); + break; + } + + if (argc < 4) + { + printf("Press enter to see a dump of your service definitions\n"); + fflush(stdout); + getc(stdin); + lp_dump(); + } + + if (argc == 4) + { + struct from_host f; + f.name = argv[2]; + f.addr = argv[3]; + + /* this is totally ugly, a real `quick' hack */ + for (s=0;s<1000;s++) + if (VALID_SNUM(s)) + { + if (allow_access(lp_hostsdeny(s),lp_hostsallow(s),&f)) + { + printf("Allow connection from %s (%s) to %s\n", + f.name,f.addr,lp_servicename(s)); + } + else + { + printf("Deny connection from %s (%s) to %s\n", + f.name,f.addr,lp_servicename(s)); + } + } + } + return(0); +} + + diff --git a/source3/utils/testprns.c b/source3/utils/testprns.c new file mode 100644 index 0000000000..89c615898d --- /dev/null +++ b/source3/utils/testprns.c @@ -0,0 +1,72 @@ +/* + Unix SMB/Netbios implementation. + Version 1.9. + test printer setup + Copyright (C) Karl Auer 1993, 1994 + + 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. +*/ + +/* + * Testbed for pcap.c + * + * This module simply checks a given printer name against the compiled-in + * printcap file. + * + * The operation is performed with DEBUGLEVEL at 3. + * + * Useful for a quick check of a printcap file. + * + */ + +#include "includes.h" +#include "smb.h" +#include "pcap.h" + +/* these live in util.c */ +extern FILE *dbf; +extern int DEBUGLEVEL; + +int main(int argc, char *argv[]) +{ + char *pszTemp; + + setup_logging(argv[0],True); + + charset_initialise(); + + if (argc < 2 || argc > 3) + printf("Usage: testprns printername [printcapfile]\n"); + else + { + dbf = fopen("test.log", "w"); + if (dbf == NULL) + printf("Unable to open logfile.\n"); + else + { + DEBUGLEVEL = 3; + pszTemp = (argc < 3) ? PRINTCAP_NAME : argv[2]; + printf("Looking for printer %s in printcap file %s\n", + argv[1], pszTemp); + if (!pcap_printername_ok(argv[1], pszTemp)) + printf("Printer name %s is not valid.\n", argv[1]); + else + printf("Printer name %s is valid.\n", argv[1]); + fclose(dbf); + } + } + return (0); +} + |